In article <9321dr$kjc$1@nntp.qnx.com>,
Mario Charest <mcharest@void_zinformatic.com> wrote:
I read the heap analysis document but couldn’t find how to
deal with the new operator.
In QNX4 I used to replace all malloc and free with a debug
version. mem_malloc and mem_free. These were macros
that passed function and line number information. Very
usefull to detect memory leak as any unfreed block would
contain a reference to the function and line number of the
corresponding mem_malloc.
I can’t get this to work using new/delete as I couldn’t find
a way to replace them with macro. I overloaded new
and delete that in turn called malloc but I have no way
to get line and function information to the overloaded new.
Any idea?
Yes. I didn’t go to the effort of providing all of the support
necessary for doing complete C++ support in the way that it is
done for C. In fact, the (checked) smart pointer template for C++ pointers
is as far as I went. However, you can get C++ to provide the
same level of information, but a macro is not necessarily the advised way
to do it.
With a default new operator – which is the one I assume you overloaded –
you could only get the return address of the caller. This works fine
in most cases, and is what the malloc_g library gives you if you
simply link against the library without modifying the application – apart
from dumping the heap on exit or whenever it is you want to detect leaks.
The malloc in the debug library does this in the following way:
void *
malloc(size_t size)
{
int line = (int)__build_return_address(0);
return debug_malloc(NULL, line, size);
}
Therefore, you can have the default new operator do the same thing
and get the program counter at the point of the leak. If you wanted
to change code you could also define a NEW macro, but there would be
no way to pass the file and line information to the new operator since
it only takes a size_t parameter.
The only way you could associate the file and line with a previously allocated
block is to find the block header and break
encapsulation by inserting the new file and line information in the
debugging part of the header – assuming you have compiled with the
malloc_g header files and linked against malloc_g. To be on the safe
side, you should call _mptr(this) to find the start of the block,
cast it to a (Dhead *) and subtract one to find the start of the
header.
i.e. inside the NEW(T,params) macro:
T *bp = new (params);
Dhead *hp = (Dhead *)_mptr(bp);
hp–;
hp->d_debug.callerpc_line = LINE;
hp->d_debug.file = FILE;
Of course, because you need a block and a return value, you need to
use a GCC extension to make the macro an expression.
Failing that, you could acquire a mutex inside the macro, set global
file and line variables, and use those in the new operator. Not very
pretty, but not as ugly as, say, pushing them on the stack and unwinding
the stack in the new operator to find them – which is an ugly possibility.
Happily, there is a better way if you compile -g and keep the symbol
information. It won’t give you file and line information, but it is
close. Include dlfcn.h in the module with the new operator
and do the following to obtain the information:
void *ptr;
void *pc = (void *)__builtin_return_address(0);
Dl_info dli;
if (dladdr(pc, &dli)) {
ptr = debug_malloc(dli.dli_sname, pc-dli.dli_saddr, size);
}
…
This would give you a symbol and an offset. Either this, or the caller
return address is more than adequate when you run the program in GDB,
because you can just list the given address to see the line.
“People looking to serious, should be looking to Sirius”
\
–
Steve Furr email: furr@qnx.com
QNX Software Systems, Ltd.