Einleitung
Besondere Mermale
Die Installation
Einbinden der Bibliothek
Umgebungsvariablen
dbmallopt()
malloc_chain_check()
malloc_dump()
malloc_list()
malloc_inuse()
malloc_mark()
malloc_abort()
malloc_enter() und malloc_leave()
Download: dbmalloc.tar.gz
Die Bibliotheck dbmalloc wird in einer freien und einer
kommerziellen Version angeboten. Die kommerzielle Version
(Virtual Technologies, Inc) ist
etwas schneller und findet auch mehr Fehler als die freie.
Intern arbeitet dbmalloc weitestgehend wie dmalloc
(ersetzt also standard funktionen durch eigene).
Original wurde sie auf ISC UNIX entwickelt und dann auf alle
anderen unterstützten Systeme portiert.
- Funktionsfluss, Datei und Zeileninformazionen werden mit angegeben
- Gibt Adressen zurück (hilfreich zusammen mit debuggern)
- Grenzberreich überprüfung
- Ausgabe geht auf Standard Error
- Findet Memory leaks
Generell wird dbmalloc, wie die meisten anderen memory tools,
mit zu einem Programm hinzugelinkt. Das bedeutet also das man
zumindest neu linken muss wenn man sein Programm mit dbmalloc
testen möchte.
Möchte man das Zeileninformationen mit bei der Ausgabe
erscheinen, so muss man zusätzlich die Datei malloc.h
mit in das zu testende Programm einbinden.
- Man sollte sicherstellen, das man die neueste Version von
dbmalloc installiert.
- Archiv entpacken.
- Nicht unter Linux: make runtests ausführen.
Dies sollte die Bibliothek übersetzen und alle Testprogramme
starten. Sollte wie auch immer irgendetwas nicht funktionieren
findet man in der README Datei eine Beschreibung die
übersetzung manuell durchzuführen.
- Unter Linux: make und danach make tests ausführen
um die Bibliothek und die testprogramme zu übersetzen. Bei problemen
hilft hier die Datei README.Linux weiter.
- Installieren der Bibliothek mit make install
Während der installation wird unter anderem auch die
eigene malloc.h (manchmal heisst diese dbmalloc.h
Datei überschrieben. Wie auch immer
sind die neuen Dateien allesamt ein guter Ersatz und man braucht
daher keine sicherheitskopien der alten anlegen.
Die Bibliothek kann in 4 verschiedenen Modi betrienen werden.
Jeder der hier aufgeführten Modi hängt von den vorhergehendem
Modi ab.
Mode 1: Bibliothek mit einbinden
Die einfachste möglichkeit die Bibliothek zu benutzen ist
sie einfach mit zu einem Programm hinzu zu linken. Dabei sollte
dbmalloc allerdings vor der standard C Bibliothek (libc.a)
stehen. Das ist normalerweise auch der Fall, wenn man diese nicht
explizit mit beim übersetzen mit angibt.
Bei dieser Methode werden eine ganze menge Fehler im Programm
gefunden werden. Allerdings werden nicht alle fähigkeiten
der Bibliothek ausgenutzt und es empfiehlt sich daher in jedem
Fall zusätzlich einige Umgebungsvariablen zu setzen, welche
auf das Verhalten von dbmalloc einfluss nehmen.
Beispiel:
gcc -o main main.c -ldbmalloc
Mode 2: einbinden der malloc.h (dbmalloc.h) Datei
In diesem Modus bindet man in seinem Programm zusätzlich
zu die malloc.h (dbmalloc.h) Datei mit ein. Diese
enthält Macros welche die Datei und Zeileninformationen bei
jeder debug funktion mit ausgeben.
Typischerweise wird man die malloc.h immer mit in seine
Programme einbinden und nur durch die
-I INCLUDEDIR
anweisung des compilers auf die von dbmalloc verweisen
wollen, um nicht jedes mal die Sourcen verändern zu
müssen.
Zu beachten ist das man das Programm ohne die malloc.h
Datei neu übersetzen muss wenn man die funktionen von
dbmalloc ausschalten möchte.
Mode 3: Laufzeit optionen setzen
Mittels Umgebungsvariablen kann man das gesamte verhalten der
Bibliothek beeinflussen. Diese möglichkeit ist ziemlich
rabiat, da man das verhalten der Bibliothek durch das setzen von
Umgebungsvariablen auf das gesamte zu testende Programm anwendet.
Manchmal weiss man aber in etwa in welchem code fragment sich
ein potentieller Fehler versteckt hat und man möchte
das Programm nur in diesem fragment testen.
Die lösung dieses Problems ist das setzen von sogenatnten
Laufzeitoptionen welche man mit der Funktion dbmallopt(..)
setzen kann. Es empfiehlt sich die Aufrufe dieser Funktion
in preprozessor bedingungen zu verschachteln damit man bei der
fehlerfreien Version des zu testendes Programms die Bibliothek
leicht wieder entfernen kann.
Beispiel:
#ifdef _DEBUG_MALLOC_INC
dbmallopt(.... );
#endif
Mode 4: Starkes untersuchen der malloc aufrufe
Dieser Modus bindet das aufrufen der speciellen dbmalloc
funktionen mit ein (leak detecktion...). Je nach gewünschter
such funktion ruft man Funktion von dbmalloc auf
und erfährt somit eine recht grosse menge von debug Informationen.
Es wird empfohlen in einer Header Datei die von allen Modulen
eingebunden wird die folgenden Zeilen mit einzufügen.
#ifndef _DEBUG_MALLOC_INC
#define malloc_enter(func)
#define malloc_leave(func)
#define malloc_chain_check()
#define malloc_dump(fd)
#define malloc_list(a,b,c)
#define malloc_inuse(hist) (*(hist) = 0, 0)
#endif
Dies wird die debug funktionen automatisch abschalten
wenn man das zu testende Programm ohne das _DEBUG_MALLOC_INC
define übersetzt wird.
Umgebungsvariablen
Umgabungsvariablen können zum verändern des generellen
Bibliotheks verhaltens benutzt werden. Die meisten dieser
Optionen kan man natürlich auch mit der dbmallopt()
Funktion gesetzt werden und sind deshalb auch nocheinmal in der
entsprechenden Sektion beschrieben.
- MALLOC_BOUNDSIZE
-
Bezeichnet das minimum an Bytes welche die allocierende
Funktion hinter dem allokiertem Berreich unbenutzt lässt.
Dieser Wert kann jeder nicht negativer ganzzahliger Wert
sein.
Diese Funktion ist nützlich wenn man vermutet dass das
zu testende Programm irgendwo hinter einen allocierten
Berreich schreibt. Standard ist für diese Variable 1.
- MALLOC_CKCHAIN
-
wenn 1, dann überprüft dbmalloc bei
jedem malloc aufruf die interne Liste.
- MALLOC_DETAIL
-
Wenn auf einen nicht negativen ganzahligen Wert gesetzt,
dann zeigt die Funktion malloc_dump einige
interne Informationen für jedes Element in der
Liste an. Diese Option ist eigentlich nur dann nützlich
wenn man die Bibliothek selbst debuggen möchte.
- MALLOC_ERRFILE
-
Gibt die Log Datei für fehler Meldungen an. Diese
Meldungen werden generell immer an die Datei angehangen.
Will man also mit einer leeren Datei einen Testlauf wiederholen,
so muss man die Datei vorher löschen oder manuell
den Inhalt abschneiden. Wenn diese Variable gesetzt ist,
werden keine Meldungen mehr auf die Standard ausgaben
(stdout, stderror) geschrieben.
- MALLOC_FATAL
-
Gibt das verhalten bei auftreten von Fehlern an.
Folgente Werte sind erlaubt:
- 0. macht mit der nächsten Anweisung weiter.
- 1. erstellt einen core und beendet das Programm.
- 2. beendet das Programm.
- 3. erstellt einen core, aber macht bei der nächsten
Anweisung weiter. Core Dateien werden in dem Format
core.[PID].[counter] gespeichert. Beispiel: core.00123.001
- 128. schreibt die malloc Liste und macht weiter.
- 129. schreibt die malloc Liste, schreibt einen core, und beended das Programm.
- 130. schreibt die malloc Liste und beended das Programm.
- 131. schreibt die malloc Liste, erstellt einen core, macht weiter.
- MALLOC_FILLAREA
-
Setzt das malloc füll gebiet flag. 4 Modi sind
erlaubt:
- 0. Schaltet das füllen und überprüfen
von gefüllten Berreichen aus. (schnellere ausführung
des Programms aber es werden auch weniger Fehler gefunden)
- 1. Schaltet das überprüfen von Grenzberreichen ein.
- 2. Beinhaltet Mode 2 und füllt allocierte Berreiche mit
einem bestimmten Wert.
- 3. Beinhaltet alles von Mode 2 (ohne 1) und füllt gefreete Berreich
mit Werten so das es zu einem Fehler bei einer erneuten benutzung
von gefreeten Adressen kommt.
Modus 0 ist am schnellsten. Modus 3 folgt Modus 0 und Modus 1 ist am
langsamsten. Standard ist Mode 3.
- MALLOC_LOWFRAG
-
Zwingt dbmalloc einen anderen Algorythmus für
das allocieren von Speicherberreichen zu benutzen.
Standardmässig nutzt dbmalloc einen schnellen aber auch
Speicher hungrigen (weil viele Fragmente entstehen) Algorythmuss.
Wie auch immer ist der langsamere aber Speicher schonendere mit
einem positiven Wert zu setzen.
- MALLOC_CKDATA
-
Schaltet die überprüfung von Zeigern an
funktionen ein/aus. Es ist typisch das der Speicherverbrauch
des Programmes beim benutzen dieser Option stark ansteigt.
- MALLOC_REUSE
-
Gibt an ob gefreete Segment wieder benutzt werden dürfen.
Diese Option kann dazu verwendet werden um fest zu stellen wo
ein einmal gefreeter Zeiger wieder benutzt wird. Es sollte
allerdings beachtet werden das mit dieser Option der Speicher
verbrauch um ein vielfaches ansteigt. Auch sollte gesagt werden
das es bei grösseren Programmen (wegen Speichermangel) zu
cores kommen kann.
- 0. Ein zweites Benutzen von gefreeten Segmenten ist nicht gestattet
und gefreete Segmente werden nicht mit einem Pattern gefüllt.
- 1. Freie Segmente werden gefüllt und können wieder benutzt werden.
- 2. Freie Segmente können wieder benutzt werden werden aber nicht gefüllt.
Diese Option lässt sich gut in kombination mit der
MALLOC_FILLAREA
Option verwenden.
- MALLOC_SHOW_LINKS
-
Zeigt das nächstes Listenelement (Link) bei auftreten eines
Fehlers mit an. Diese Option macht Sinn da oft die vorhergenende
Operation den Wirklichen Fehler provoziert hat.
- 0. Zeigt die Links nicht an.
- 1. Zeigt sie an.
- MALLOC_WARN
-
Gibt das Warn und Error verhalten an.
Weisst dbmalloc an vor und hinter einem allociertem Berreich jeweils
8 Bytes zu allocieren, nach jeder speicher Operation zu überprüfen
und die Ausgabe in die Datei dbmallog.log zu schrieben.
setenv MALLOC_BOUNDSIZE "8"
|
setenv MALLOC_ERRFILE "dbmalloc.log"
|
setenv MALLOC_CKCHAIN 1
|
Die Funktion dbmallopt(..) kann zu jeder Zeit in einem
Programm zum setzen von debug Optionen
benutzt werden. Folgende Optionen sind dabei gültig:
- MALLOC_WARN
-
Setzt das verhalten bei Fehlern. Der Wert dieser
Option ist ein integer welcher mit folgenden
Konstanten umschrieben werden kann:
- M_HANDLE_IGNORE gibt eine Warnung aus, aber ignoriert auftretende Fehler.
- M_HANDLE_ABORT erstellt einen core und beendet das Programm.
- M_HANDLE_EXIT beendet das Programm.
- M_HANDLE_CORE erstellt einen core aber fährt mit der Ausführung fort.
Standard ist M_HANDLE_IGNORE
- MALLOC_FATAL
-
Setzt das Verhalten bei Errors. Der setzbare Wert ist
gleich dem bei MALLOC_WARN.
Standard ist M_HANDLE_ABORT.
- MALLOC_ERRFILE
-
Ist das gleiche wie bei der Umgebungsvariable
MALLOC_ERRFILE.
Gibt also die Datei an in welche alle Meldungen der Bibliothek geschrieben
werden. Ist diese Option nicht gesetzt, gehen alle Meldungen zu
stdandard error.
- MALLOC_CKCHAIN
-
Ist das gleiche wie bei der Umgebungsvariable
MALLOC_CKCHAIN.
Setzt also das chain checking flag welches dbmalloc
dazu anweist bei jedem malloc call die verkettete innere Liste
zu überprüfen.
- MALLOC_FREEMARK
-
Setzt das Verhalten bei einem Free auf ein Markiertes
Segment. Normalerweise generiert das eine Warnung, ist
diese Option 0 dann werden diese Warnungen nicht erstellt.
- MALLOC_FILLAREA
-
Ist das gleiche wie bei der Umgebungsvariable
MALLOC_FILLAREA.
Gibt also an wie gefreete Segmente gefüllt werden sollen.
- MALLOC_LOWFRAG
-
Ist das gleiche wie bei der Umgebungsvariable
MALLOC_LOWFRAG.
Gibt also an welcher Algorythmus zum allokieren verwendet werden
soll.
- MALLOC_CKDATA
-
Ist das gleiche wie bei der Umgebungsvariable
MALLOC_CKDATA.
Gibt also an ob funktions parameter überprüft werden
sollen.
- MALLOC_REUSE
-
Ist das gleiche wie bei der Umgebungsvariable
MALLOC_REUSE.
Gibt also an ob gefreete Segmente erneut benutzt werden dürfen.
-
Um z.B. eine Testumgebung zu schaffen welche bei jeder Warnung einen
core erstellt, bei einem Error einen core und danach das Programm Beendet,
und alle Nachrichten in die Datei malloc_log umzuleiten, ist folgender
Quelltext notwendig:
#include < malloc.h >
union dbmalloptarg m;
m.i = M_HANDLE_CORE | M_HANDLE_DUMP;
dbmallopt(MALLOC_WARN,m);
m.i = M_HANDLE_ABORT;
dbmallopt(MALLOC_FATAL,m);
m.str = "malloc_log";
dbmallopt(MALLOC_ERRFILE,m);
|
Checkt den Zustand der internen Verketteten malloc liste.
Bei einem schwerwiegendem Fehler erzeugt der aufruf der
Funktion einen error. Anderenfalls gibt sie 0 zurück
wenn alles Ok ist und einen Wert ungleich 0 bei einem
leichtem Fehler.
Schreibt alle benutzten Speichersegmente (die ersten Bytes)
raus. Wenn die Umgebungsvariable
MALLOC_DETAIL
auf einen Wert ungleich 0 gesetzt ist werden alle Segmente
(auch die gefreeten) aufgelistet. fd ist der Dateidescriptor
in welchen die geschrieben wird.
Schreibt eine Liste in dem selben Format wie malloc_dump(). Allerdings
werden nur die Segmente ausgegeben welche wirklich genutzt werden und
in dem angebenen Zeit Berreich von histid1 bis histid2 sind.
fdist der Dateidescriptor in welchen die geschrieben wird.
Gibt die Summe der momentan benutzten Speichersegmente in Bytes
zurück. Wenn histidptr ungleich NULL ist, dann
wird an der Speicherstelle die aktuelle history ID gespeichert welche
dann zusammen mit malloc_list() verwendet werden kann um alle
momentan benutzten Elemente auflisten zu lassen. Das nächste Beispiel
zeigt wie man die Funktionen typischerweise einsetzt um Memory leaks
zu finden:
unsigned long histid1, histid2, orig_size, current_size;
orig_size = malloc_inuse(&histid1);
/* ..... go do lots of stuff ...... */
current_size = malloc_inuse(&histid2);
if( current_size != orig_size ) {
malloc_list(2,histid1,histid2);
}
|
Als Ausgabe bekommt man dann zum Beispiel so etwas:
************************** Dump of Malloc Chain ****************************
POINTER FILE WHERE LINE ALLOC DATA HEX DUMP
TO DATA ALLOCATED NUMBER FUNCT LENGTH OF BYTES 1-7
-------- -------------------- ------- -------------- ------- --------------
080509AC main.c 14 malloc(1) 10 31323334353637
08050A00 main.c 8 malloc(2) 10 31323334353637
|
Markiert ein Segment als Memory leak frei. Dies kann genutzt werden um
Zeiger zu Markieren welche einmal allociert für immer existieren
und nur störend bei der Ausgabe sind. Ein netter nebeneffeckt
ist das die Leak liste kleiner wird und das Programm damit auch schneller.
Diese Funktion erstellt beim aufruf einen core und beendet das Programm.
Um dies zu tun ruft die Funktion ganz einfach abort auf. Wie auch
immer kann die Funktion benutzt werden um eine einfache Error routine
für eigene zwecke zu haben.
Diese beiden Funktionen stellen einen einfachen Weg da den Inhalt des Stacks
aus zu geben. malloc_enter() muss dabei beim betreten und
malloc_leave() beim verlassen einer Funktion aufgerufen werden.
Vergisst man allerdings einen Aufruf von malloc_leave()
wird das mit einer Fehlermeldung und einem Programmabbruch quitiert.
|