This is substantially different from counting leak detectors, which simply verify that all allocated objects are eventually deallocated. A garbage-collector based leak detector can provide somewhat more precise information when an object was leaked. More importantly, it does not report objects that are never deallocated because they are part of "permanent" data structures. Thus it does not require all objects to be deallocated at process exit time, a potentially useless activity that often triggers large amounts of paging.
All non-ancient versions of the garbage collector provide leak detection support. Version 5.0 alpha 3 adds the following features:
To use the collector as a leak detector, follow the following steps:
For more precise error reports, as much of the program as possible should use the all uppercase variants of these functions, after defining GC_DEBUG, and then including gc.h. In this environment GC_MALLOC is a macro which causes at least the file name and line number at the allocation point to be saved as part of the object. Leak reports will then also include this information.
On a few platforms (currently Solaris and Irix), GC_MALLOC also causes some more information about its call stack to be saved in the object. Such information is reproduced in the error reports in very non-symbolic form, but it can be very useful with the aid of a debugger.
#define GC_DEBUG #include "gc.h" #define malloc(n) GC_MALLOC(n) #define calloc(m,n) GC_MALLOC(m*n) #define free(p) GC_FREE(p) #define realloc(p,n) GC_REALLOC(n) #define CHECK_LEAKS() GC_gcollect()
Assume the collector has been built with -DFIND_LEAK. (For very new versions of the collector, we could instead add the statement GC_find_leak = 1 as the first statement in main.
The program to be tested for leaks can then look like:
#include "leak_detector.h" main() { int *p[10]; int i; /* GC_find_leak = 1; for new collect versions not compiled */ /* with -DFIND_LEAK. */ for (i = 0; i < 10; ++i) { p[i] = malloc(sizeof(int)+i); } for (i = 1; i < 10; ++i) { free(p[i]); } for (i = 0; i < 9; ++i) { p[i] = malloc(sizeof(int)+i); } CHECK_LEAKS(); }
On an Intel X86 Linux system this produces on the stderr stream:
Leaked composite object at 0x806dff0 (leak_test.c:8, sz=4)On Irix it reports
Leaked composite object at 0x10040fe0 (leak_test.c:8, sz=4) Caller at allocation: ##PC##= 0x10004910and on Solaris the error report is
Leaked composite object at 0xef621fc8 (leak_test.c:8, sz=4) Call chain at allocation: args: 4 (0x4), 200656 (0x30FD0) ##PC##= 0x14ADC args: 1 (0x1), -268436012 (0xEFFFFDD4) ##PC##= 0x14A64In the latter two cases some additional information is given about how malloc was called when the leaked object was allocated. For Solaris, the first line specifies the arguments to GC_debug_malloc (the actual allocation routine), The second the program counter inside main, the third the arguments to main, and finally the program counter inside the caller to main (i.e. in the C startup code).
In the Irix case, only the address inside the caller to main is given.
In all cases, a debugger is needed to interpret the additional information. On systems supporting the "adb" debugger, the callprocs script can be used to replace program counter values with symbolic names.