logo http://dickey.his.com/dbmalloc/dbmalloc.html/

dbmalloc

Autor: Gerrit Bruchhäuser

Quellenverzeichnis:

Die Homepage

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


Einleitung

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.

Original wurde sie auf ISC UNIX entwickelt und dann auf alle anderen unterstützten Systeme portiert.

Intern überschreibt die Bibliotheck die berreits vorhandenen funktionen malloc, calloc, realloc, strdup, free und noch einige mehr, um Fehler in der Benutzung dieser Funktionen erkennen zu können. Dabei benutzt die Bibliotheck am liebsten die Aufrufe eigenen Funktionen, die zusätzlich Datei- und Zeileninformationen mit verarbeiten können. Um das zu erreichen benutzt dbmalloc den C preprocessor und ersetzt die vorhandenen Funktionsaufrufe durch eigene. So sieht zum Beispiel das Macro von malloc, welches in der Datei malloc.h definiert ist, so aus:

#define malloc(len) debug_malloc( __FILE__,__LINE__, (len))

Die implementation von debug_malloc findet man dann in der C Datei malloc.c.

Besondere Merkmale

  • Funktionsfluss, Datei und Zeileninformationen 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.

Um dies zu realisieren bedient sich dbmalloc der ausgeprägten macro funktionalität, und überschreibt ganz einfach

Die Installation

  1. Man sollte sicherstellen, das man die neueste Version von dbmalloc installiert.
  2. Archiv entpacken.
  3. 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.
  4. 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.
  5. 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.

Einbinden der Bibliothek

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

MALLOC_BOUNDSIZE MALLOC_CKCHAIN MALLOC_DETAIL MALLOC_ERRFILE MALLOC_FATAL MALLOC_FILLAREA MALLOC_FILLBYTE MALLOC_FREEBYTE MALLOC_LOWFRAG MALLOC_CKDATA MALLOC_REUSE MALLOC_SHOW_LINKS MALLOC_SHOW_LINKS MALLOC_WARN

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.
  • 0. aus
  • 1. ein (Standard)
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

dbmallopt()

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);

malloc_chain_check()

int malloc_chain_check(int flag);

Checkt den Zustand der internen Verketteten malloc liste. Ist flag ungleich 0, so wird bei einem schwerwiegendem Fehler einen fatalen error erzeugt. Anderenfalls gibt sie 0 zurück wenn alles Ok ist und einen Wert ungleich 0 bei einem Fehler.

Die deklaration steht wie folgt in malloc.h:

#define malloc_chain_check(do) DBmalloc_chain_check(__FILE__,__LINE__,(do))

Wobei die implementation von DBmalloc_chain_check in der Datei mchain.c zu finden ist.

malloc_dump()

void malloc_dump(int fd);

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 geschrieben wird.

Die Deklaration steht in malloc.h, wobei der Funbktionsrumpf in der dump.c Datei zu finden ist.

malloc_list()

void malloc_list(int fd, unsigned long histid1, unsigned long histid2);

Schreibt eine Liste in dem selben Format wie malloc_dump(). Allerdings werden nur die Segmente ausgegeben welche wirklich genutzt werden und in dem angebenen Zeitberreich von histid1 bis histid2 liegen. fd ist dabei der Dateidescriptor in welchen geschrieben wird.

Diese funktion arbeitet mit der Funktion malloc_inuse() zusammen, welche den aktuellen Stand allocierter Berreiche in einer histid2 (history ID) zusammenfasst, welche dann hier eingesetzt werden kann.

Die Deklaration steht in malloc.h, wobei der Funbktionsrumpf in der dump.c Datei zu finden ist.

malloc_inuse()

unsigned long malloc_inuse(unsigned long *histidptr);

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);


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

Deklarariert ist diese Funktion in der malloc.h Datei, wobei der Funktionsrumpf in der Datei leak.c zu finden ist.

malloc_mark()

void malloc_mark(char *ptr);

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 wird.

Die deklaration ist wie folgt in malloc.h definiert:

#define malloc_mark(ptr) DBmalloc_mark(__FILE__,__LINE__,(ptr))

Wobei die implementation von DBmalloc_mark in der Datei leak.c zu finden ist.

malloc_abort()

void malloc_abort();

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.

malloc_enter() und malloc_leave()

void malloc_enter(const char *func);

void malloc_leave(const char *func);

Diese beiden Funktionen stellen einen einfachen Weg da den Inhalt des Stacks mit zu schneiden. 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. Die Ausgabe des Funktionsflusses geschieht dann bei jeder Fehlerausgabe.

 This error is *probably* associated with the following allocation:

	A call to malloc for 1 bytes in teststack.c on line 75.
	This was the 13th call to malloc.
	Stack from where allocated:
	 -> sub3() in teststack.c(73)
	 -> sub2() in teststack.c(59)
	 -> main() in teststack.c(23)

Die deklaration der beiden Funktionen ist in malloc.h wie folgt definiert:

#define malloc_enter(func) StackEnter(func, __FILE__, __LINE__)

#define malloc_leave(func) StackLeave(func, __FILE__, __LINE__)

Wobei die implementation der Funktionsrümpfe in der Datei stack.c zu finden ist.

Wichtig: Die parameter der beiden Funktionen müssen in jedem fall const oder static sein, da die stack funktionen die Parameter nicht expliziet kopieren.

Copyright (c) 2001-2002 by: Gerrit Bruchhäuser