Using
Electric Fence to Debug Selected
Allocations

Electric Fence is a
Red-Zone memory allocator written by Bruce Perens.
It provides a special version of malloc()
and similar functions for
debugging software that is suspected of overrunning or underrunning the
boundaries of a malloc buffer, or touching free memory. It arranges for
each malloc buffer to be followed (or preceded) in the address space by
an inaccessable virtual memory page, and for free memory to be
inaccessable. If software touches the inaccessable page, it will get an
immediate segmentation fault. It is then trivial to uncover the
offending code using a debugger. An advantage of this product over most
malloc debuggers is that this one detects reading out of bounds as well
as writing, and this one stops on the exact instruction that causes the
error, rather than waiting until the next boundary check. A Secondary
advantage is that it can be used as a replacement of the standard
malloc, thereby debugging software that cannot be recompiled or
tracking allocations within libraries themselves.
This however
forms immediatelly the problem I encountered. When debugging my
pet-project BpmDj, I found
that the QT libraries use extravagant many
memory allocations, making the use of electric fence impossible, due to
its memory consumption (2 pages = 16K for every single allocation !).
Nevertheless, the thing I wanted to do was to check the memory chunks
that I had allocated, not those allocated by others. Below we explain
how this can easily be done with
electric fence.
Step
1: change
all malloc's, realloc's and frees in your software with own versions.
E.g: allocate,
reallocate, deallocate. Prototypes of
such functions can be found in the file common.h
#define
allocate(size,
type) (type*)
bpmdj_alloc(sizeof(type)*(size), __FILE__, __LINE__)
#define
array(name,size,type)
type* name = allocate(size,type)
#define
reallocate(thing, size, type) (type*)
bpmdj_realloc(thing,sizeof(type)*(size))
#define
deallocate(thing) bpmdj_free(thing);
void *
bpmdj_alloc(int size, char* file, int line);
void *
bpmdj_realloc(void* thing, int size);
void
bpmdj_free(void*);
Step
2: modify
the library to NOT replace the standard memory operations. The only
files needed from the electric fence package are efence.h page.c,
efence.c and efence-print.cpp
Step
2.1:
modify all references to malloc, calloc, realloc and free in efence.c
to efence_malloc, efence_calloc, efence_realloc and efence_free. Make
sure that prototypes for these functions are added into efence.h.
void *
efence_realloc(void * oldBuffer, size_t newSize)
{
void * newBuffer = efence_malloc(newSize);
...
efence_free(oldBuffer);
...
}
void *
efence_malloc(size_t size)
{
...
}
void *
efence_calloc(size_t nelem, size_t elsize)
{
...
}
Step
2.2:
modify the efence_free function to return a boolean. If the freeing was
successfull true should be returned. If not because the pointer itself
was not within range of the efence allocated memory thenen it should
return false. This will later prove to be very usefull to write a
generic deallocate function.
bool
efence_free(void * address)
{
...
if ( address == 0 )
{
unlock();
return true;
}
...
if ( !slot )
{
unlock();
return false;
}
// removed EF_Abort("efence_free(%a):
address not from efence_malloc().", address);
...
return true;
}
Step
2.3: the
standard efence-print library makes ues of its own printing functions.
These can be safely removed because the standard C library functions no
longer pose a reentrance thread. Thus, remove printNumber and vprint. Replace all vprint
calls with vprintf and include the
necessary #include <stdlib.h>
Step
2.4:
change efence-print.cpp, efence.cpp, page.cpp, efence.h by putting an
#ifdef EFENCE in front and an #endif in the back of the file. This will
make it possible to remove the entire EFENCE from compiling.
Step
3: write
your own memory functions as declared in step 1. These are
straightforward, except for the deallocate function. Here we must
differentiate between efence-allocated chunks and an standard-malloced
chunks. This can be done as follows.
void
bpmdj_free(void* a)
{
#ifdef EFENCE
if
(!efence_free(a))
#endif
free(a);
}
void*
bpmdj_alloc(int length, char* file, int line)
{
void *
result;
assert(length>=0);
#ifdef EFENCE
result
= efence_malloc(length);
#else
result
= malloc(length);
#endif
if
(!result)
printf("Error: %s(%d): unable to allocate %d bytes
\n",file,line,length);
assert(result);
return
result;
}
void*
bpmdj_realloc(void* thing, int size)
{
void *
result;
assert(size);
#ifdef EFENCE
result
= efence_realloc(thing,size);
#else
result
= realloc(thing,size);
#endif
assert(result);
return
result;
}
This should do
the trick. To use it simply put the object files efence.o, page.o,
efence-print.o and common.o into your program. When you want checks
pass -DEFENCE to the compiler, in the other case simply ommit it.
These files can
be downloaded here. This should make it
possible to debug QT
applications easily under linux.