 |
BorlandTalk.com Borland discussion newsgroups
|
| View previous topic :: View next topic |
| Author |
Message |
Carlos Guest
|
Posted: Tue Dec 13, 2005 4:13 pm Post subject: Can I override the global new/delete in C++Builder 6 ? |
|
|
Hi,
I was trying to overload the global new and delete to add some memory check
to my application. The code was added in VCL.h and it was fine for a simple
test application that had only a form and some buttons to create and delete
char and int arrays. This was fine till I compiled a more complex app. In
that app something includes utilcls.h and that didn't like the new/delete
overload. Am I doing the right thing ? Is there another way to do what I am
trying to do ? Here is the code that was added to VCL.h... Thanks for
helping :)
Carlos Mejia
#ifndef VCL_H
#define VCL_H
#define INC_VCL
#include <basepch0.h>
TStringList *gAllocListInfo = NULL;
struct PTR_INFO
{
DWORD addr;
DWORD size;
};
void AddTrack( DWORD addr, DWORD asize, const char *fname, DWORD lnum )
{
if( !gAllocListInfo ) {
gAllocListInfo = new TStringList;
}
PTR_INFO *info = (PTR_INFO *)malloc(sizeof(PTR_INFO));
info->addr = addr;
info->size = asize;
gAllocListInfo->AddObject( String("File: ") + String(fname) +
String("Line: ") + String(lnum) +
String("Address: ") + String(addr) +
String("Size: ") + String(asize),
(TObject*)info );
};
void RemoveTrack( DWORD addr )
{
if( !gAllocListInfo )
return;
for( int i = 0; i < gAllocListInfo->Count; i++ )
{
PTR_INFO *info = (PTR_INFO*)gAllocListInfo->Objects[i];
if( info->addr == addr )
{
free(info);
gAllocListInfo->Delete(i);
break;
}
}
};
void DumpUnfreed( void )
{
DWORD totalSize = 0;
if( !gAllocListInfo )
return;
for( int i = 0; i < gAllocListInfo->Count; i++ )
{
PTR_INFO *info = (PTR_INFO*) gAllocListInfo->Objects[i];
totalSize += info->size;
gAllocListInfo->Objects[i] = NULL;
free(info);
}
gAllocListInfo->Add( String("=======================n") );
gAllocListInfo->Add( String("Total: ") + String(totalSize) );
gAllocListInfo->SaveToFile(".////MemDump.txt");
delete gAllocListInfo;
};
#ifdef _DEBUG
void * _cdecl operator new( size_t size, const char *file, int line )
{
void *ptr = (void *)malloc(size);
AddTrack((DWORD)ptr, size, file, line);
return(ptr);
};
void _cdecl operator delete( void *p )
{
RemoveTrack( (DWORD)p );
free(p);
};
void * _cdecl operator new[] ( size_t size, const char *file, int line )
{
void *ptr = (void *)malloc(size);
AddTrack((DWORD)ptr, size, file, line);
return(ptr);
}
void _cdecl operator delete[] ( void * p )
{
RemoveTrack( (DWORD)p );
free(p);
}
#endif
#ifdef _DEBUG
#define DEBUG_NEW new( __FILE__, __LINE__ )
#else
#define DEBUG_NEW new
#endif //_DEBUG
#define new DEBUG_NEW
#endif // VCL_H
|
|
| Back to top |
|
 |
Thomas Maeder [TeamB] Guest
|
Posted: Tue Dec 13, 2005 7:19 pm Post subject: Re: Can I override the global new/delete in C++Builder 6 ? |
|
|
"Carlos" <cmejia (AT) trellianetworks (DOT) com> writes:
| Quote: | I was trying to overload the global new and delete to add some
memory check to my application. The code was added in VCL.h and it
was fine for a simple test application that had only a form and some
buttons to create and delete char and int arrays. This was fine
till I compiled a more complex app. In that app something includes
utilcls.h and that didn't like the new/delete overload.
|
First of all, what you are doing (or attempting) is in fact
overloading, and not overriding. Your post is off-topic :-)
Second: what does "didn't like" mean?
| Quote: | Am I doing the right thing ? Is there another way to do what I am
trying to do ? Here is the code that was added to VCL.h... Thanks
for helping
|
Then, I'm not sure if modifying vcl.h is such a good idea (actually,
I'm pretty sure that it isn't).
| Quote: | #ifndef VCL_H
#define VCL_H
#define INC_VCL
#include <basepch0.h
TStringList *gAllocListInfo = NULL;
|
You could simplify your code by changing this to
extern std::auto_ptr
and adding
std::auto_ptr<TStringList> gAllocListInfo(new TStringList);
to some translation unit where the original operator new is used.
| Quote: | struct PTR_INFO
{
DWORD addr;
|
void * would be a much nicer type for this member.
| Quote: | DWORD size;
};
void AddTrack( DWORD addr, DWORD asize, const char *fname, DWORD lnum )
{
if( !gAllocListInfo ) {
gAllocListInfo = new TStringList;
}
PTR_INFO *info = (PTR_INFO *)malloc(sizeof(PTR_INFO));
|
I'd suggest that you never use C style casts in new code. C++ cast
operators are so much more expressive.
| Quote: | info->addr = addr;
info->size = asize;
gAllocListInfo->AddObject( String("File: ") + String(fname) +
String("Line: ") + String(lnum) +
String("Address: ") + String(addr) +
String("Size: ") + String(asize),
(TObject*)info );
|
This cast looks like a rather bad lie.
| Quote: | };
void RemoveTrack( DWORD addr )
{
if( !gAllocListInfo )
return;
for( int i = 0; i < gAllocListInfo->Count; i++ )
|
Ouch. This is going slow down every program large enough to make
applying this debugging device useful. See below for an alternative
design.
| Quote: | {
PTR_INFO *info = (PTR_INFO*)gAllocListInfo->Objects[i];
if( info->addr == addr )
{
free(info);
gAllocListInfo->Delete(i);
break;
}
}
};
void DumpUnfreed( void )
{
DWORD totalSize = 0;
if( !gAllocListInfo )
return;
for( int i = 0; i < gAllocListInfo->Count; i++ )
{
PTR_INFO *info = (PTR_INFO*) gAllocListInfo->Objects[i];
totalSize += info->size;
gAllocListInfo->Objects[i] = NULL;
free(info);
}
gAllocListInfo->Add( String("=======================n") );
gAllocListInfo->Add( String("Total: ") + String(totalSize) );
gAllocListInfo->SaveToFile(".////MemDump.txt");
delete gAllocListInfo;
};
#ifdef _DEBUG
void * _cdecl operator new( size_t size, const char *file, int line )
{
void *ptr = (void *)malloc(size);
|
Why do you cast void * to void *?
| Quote: | AddTrack((DWORD)ptr, size, file, line);
return(ptr);
};
void _cdecl operator delete( void *p )
{
RemoveTrack( (DWORD)p );
free(p);
};
void * _cdecl operator new[] ( size_t size, const char *file, int line )
{
void *ptr = (void *)malloc(size);
|
Why do you cast void * to void *?
| Quote: | AddTrack((DWORD)ptr, size, file, line);
return(ptr);
}
void _cdecl operator delete[] ( void * p )
{
RemoveTrack( (DWORD)p );
free(p);
}
|
In Standard C++, you also need the placement forms of delete and
delete[] corresponding to the placement forms of new and new[] you
provide here. These placement forms of delete and delete[] will be
invoked if the construction of an object whose memory was provided by
the placement new fails.
| Quote: | #endif
#ifdef _DEBUG
#define DEBUG_NEW new( __FILE__, __LINE__ )
#else
#define DEBUG_NEW new
#endif //_DEBUG
#define new DEBUG_NEW
|
I wonder whether this could backfire if some class attempts to
overload operator new() or some other placement new is (attempted to
be) created.
Personally, I'd try to keep track of the allocated chunks in a doubly
linked list:
#include <new>
#include <cstdio>
#include <cstdlib>
#define TRACK_NEW
#ifdef TRACK_NEW
struct AllocInfo
{
AllocInfo *next_;
AllocInfo *prev_;
std::size_t size_;
char const *file_;
int line_;
static AllocInfo head;
static AllocInfo tail;
static unsigned long count;
AllocInfo(); // for head
AllocInfo(AllocInfo &); // for tail
AllocInfo(std::size_t size, char const *file, int line); // for allocs
~AllocInfo();
};
//static
AllocInfo AllocInfo::head;
//static
AllocInfo AllocInfo::tail(head);
//static
unsigned long AllocInfo::count(0);
AllocInfo::AllocInfo()
: next_(0)
, prev_(this) // prevent destructor from crashing
, size_(0)
, file_(0)
, line_(0)
{
++count;
}
AllocInfo::AllocInfo(AllocInfo &ai)
: next_(this) // prevent destructor from crashing
, prev_(&ai)
, size_(0)
, file_(0)
, line_(0)
{
ai.next_ = this;
++count;
}
AllocInfo::AllocInfo(std::size_t size, char const *file, int line)
: next_(head.next_)
, prev_(&head)
, size_(size)
, file_(file)
, line_(line)
{
head.next_ = this;
next_->prev_ = this;
++count;
}
AllocInfo::~AllocInfo()
{
prev_->next_ = next_;
next_->prev_ = prev_;
--count;
}
void *operator new(std::size_t size, const char *file, int line)
{
void * const ptr(std::malloc(size + sizeof(AllocInfo)));
new (ptr) AllocInfo(size,file,line);
return static_cast<unsigned char *>(ptr) + sizeof(AllocInfo);
}
void operator delete(void *p)
{
if (p!=0)
{
AllocInfo * ai;
void *const adjusted(static_cast<unsigned char *>(p) - sizeof *ai);
ai = static_cast<AllocInfo *>(adjusted);
ai->~AllocInfo();
std::free(ai);
}
}
void operator delete(void *p, const char *file, int line)
{
::operator delete(p);
}
void *operator new[](std::size_t size, const char *file, int line)
{
return ::operator new(size,file,line);
}
void operator delete[](void *p)
{
::operator delete(p);
}
void operator delete[](void *p, const char *file, int line)
{
::operator delete(p,file,line);
}
struct Dumper
{
~Dumper()
{
std::printf("%lu chunk(s) leaked:n",AllocInfo::count);
for (AllocInfo *ai = AllocInfo::head.next_;
ai!=&AllocInfo::tail;
ai = ai->next_)
std::printf("%lu bytes allocated in file %s on line %dn",
static_cast<unsigned long>(ai->size_),
ai->file_,
ai->line_);
}
} theDumper; // define after head and tail or they will already be destructed
#endif
#ifdef TRACK_NEW
#define DEBUG_NEW new (__FILE__,__LINE__)
#else
#define DEBUG_NEW new
#endif //TRACK_NEW
#define new DEBUG_NEW
struct Throws
{
Throws() { throw 1; }
};
int main()
{
delete new int;
new int;
try
{
new Throws;
}
catch (...)
{
std::printf("caughtn");
}
}
You'd obviously declare the six operators plus the #ifdef scaffolding
in some header and make sure it is #included before any other
header. And hide AllocInfo, Dumper and the operator implementations in
some translation unit.
Disclaimer:
* This is not very well tested
* I have no idea how this behaves in connection with DLLs
* And there may be alignment issues; the result of operator new() may
not be properly aligned for all types. You'd have to fiddle with the
size of AllocInfo if that's a problem
|
|
| Back to top |
|
 |
Remy Lebeau (TeamB) Guest
|
Posted: Tue Dec 13, 2005 8:39 pm Post subject: Re: Can I override the global new/delete in C++Builder 6 ? |
|
|
"Carlos" <cmejia (AT) trellianetworks (DOT) com> wrote
| Quote: | The code was added in VCL.h
|
You should not have done that. Do NOT modify the existing header files.
Put your code in your own files.
| Quote: | In that app something includes utilcls.h and that didn't like
the new/delete overload.
|
Can you be more specific?
| Quote: | TStringList *gAllocListInfo = NULL;
|
You are declaring a global pointer in a header file. Every unit that
includes that header will get its own copy of that pointer. The pointer
will not be shared across units at all. You should be declaring the pointer
as 'extern' in a header file, and then declare the real pointer in a .cpp
file instead, ie:
--- My.h ---
extern TStringList *gAllocListInfo;
--- My.cpp ---
#include "My.h"
TStringList *gAllocListInfo = NULL;
I would also suggest not using a VCL container in the first place, since it
will force all of your projects to have a dependancy on the VCL. A linked
list, for example, would be more efficient and portable.
| Quote: | PTR_INFO *info = (PTR_INFO *)malloc(sizeof(PTR_INFO));
|
You are not checking to make sure that malloc() succeeds before accessing
the allocated memory.
| Quote: | gAllocListInfo->AddObject( String("File: ") + String(fname) +
String("Line: ") + String(lnum) +
String("Address: ") + String(addr) +
String("Size: ") + String(asize),
|
That is going to produce a trace string that has no separators between the
various values, ie:
"File: My.cppLine: 12345Address: 1234567890Size: 123456"
You should put in some commas or line breaks or something to make it easiesr
to read.
| Quote: | #ifdef _DEBUG
#define DEBUG_NEW new( __FILE__, __LINE__ )
#else
#define DEBUG_NEW new
#endif //_DEBUG
#define new DEBUG_NEW
|
I would not recommend that approach.
First, it will potentially mess up the VCL since you would be forcing the C
memory manager to be used for all VCL allocations in your code instead of
the VCL memory manager. In order to track VCL memory allocations, you would
have to subclass the VCL's memory manager via the Get/SetMemoryManager()
functions.
Second, using a 'define' statement will have no effect on pre-compiled code
that did not already use your overloaded 'new' operator. So you probably
won't be able to track any memory that is allocated/free by pre-compiled
libraries.
Third, you are using malloc() to allocate all memory. Your code is not
portable because there is no guaratee that malloc() is not implemented in
terms of 'new' internally. When that happens, you may have problems with
recursion and such.
Also, the standard 'new' operator calls a global callback function when the
allocation fails. Your overloaded overators won't be able to do that unless
they call the standard operators directly.
And lastly, it is not thread-safe. If you call 'new' in multiple threads,
then you need to provide synchronization to your tracking list.
If you must track the memory yourself, then try the following (untested, and
no thread-safety provided for simplicity):
--- MyNew.h ---
#ifndef MyNewH
#define MyNewH
#ifdef _DEBUG
void* operator new(size_t size, const char *file, unsigned int line);
void operator delete(void *p);
void* operator new[] (size_t size, const char *file, unsigned int line);
void operator delete[] (void * p);
void DumpUnfreed(void);
#endif
#endif
--- MyNew.cpp ---
#include "MyNew.h"
#ifdef _DEBUG
struct PTR_INFO
{
PTR_INFO *previous;
PTR_INFO *next;
void *addr;
size_t size;
unsigned int line;
char name[1];
};
PTR_INFO *gAllocFirst = NULL;
PTR_INFO *gAllocLast = NULL;
void* AddTrack(size_t size, bool isArray, const char *name, unsigned int
line)
{
void *ptr;
if( isArray )
ptr = ::operator new[](size);
else
ptr = ::operator new(size);
size_t len = strlen(name);
PTR_INFO *info;
try
{
info = (PTR_INFO *) ::new char[sizeof(PTR_INFO) + len];
}
catch(...)
{
if( isArray )
::operator delete[](ptr);
else
::operator delete(ptr);
throw;
}
info->previous = NULL;
info->next = NULL;
info->addr = ptr;
info->size = size;
info->line = line;
strncpy(info->name, name, len);
info->name[len] = ' ';
if( !gAllocFirst )
gAllocFirst = info;
if( gAllocLast )
{
gAllocLast->next = info;
info->previous = gAllocLast;
gAllocLast = info;
}
else
gAllocLast = info;
return ptr;
}
void RemoveTrack(void *addr)
{
PTR_INFO *info = gAllocFirst;
while( info )
{
if( info->addr == addr )
{
if( info->previous )
info->previous->next = info->next;
if( info->next )
info->next->previous = info->previous;
if( info == gAllocFirst )
gAllocFirst = info->next;
if( info == gAllocLast )
gAllocLast = info->previous;
::delete[] (char*) info;
return;
}
info = info->next;
}
}
void DumpUnfreed(void)
{
DWORD totalSize = 0;
FILE *f = fopen("./MemDump.txt", "wt");
PTR_INFO *info = gAllocFirst;
while( info )
{
totalSize += info->size;
if( f )
{
fprintf(f, "File: %s, Line: %u, Addr: %p, Size: %u",
info->name, info->line, info->addr, info->size);
}
PTR_INFO *temp = info;
info = info->next;
::delete[] (char*) temp;
}
if( f )
{
fprintf(f, "=======================nTotal: %u", totalSize);
fclose(f);
}
gAllocFirst = gAllocLast = NULL;
}
void* operator new(size_t size, const char *file, unsigned int line)
{
return AddTrack(size, false, file, line);
}
void operator delete(void *p)
{
RemoveTrack(p);
::operator delete(p);
}
void* operator new[] (size_t size, const char *file, unsigned int line)
{
return AddTrack(size, true, file, line);
}
void operator delete[](void * p)
{
RemoveTrack(p);
::operator delete[](p);
}
#define new new(__FILE__, __LINE__)
#endif
With that said, BCB already has a memory checker available. Go into the
Project Options and enable CodeGuard. It tracks all memory allocations for
you, and produces a .cgl file at program termination. It also track memory
operations during runtime to make sure that freed pointers aren't being
reused, pointer overruns aren't occuring, etc.
Gambit
|
|
| Back to top |
|
 |
Carlos Guest
|
Posted: Wed Dec 14, 2005 7:44 pm Post subject: Re: Can I override the global new/delete in C++Builder 6 ? |
|
|
Hi,
| Quote: | In that app something includes utilcls.h and that didn't like
the new/delete overload.
|
There was a class in utilcls.h that had, in the private part of the class, a
new and a delete and that didn't compile because it was looking for the old
new(size_t) most probably.
| Quote: | With that said, BCB already has a memory checker available. Go into the
Project Options and enable CodeGuard. It tracks all memory allocations
for
you, and produces a .cgl file at program termination. It also track
memory
operations during runtime to make sure that freed pointers aren't being
reused, pointer overruns aren't occuring, etc.
|
I also tried CodeGuard but that gives me some warning and my app doesn't
work after that. This is the only warning that I have:
Two different CRTLDLLs are loaded. CG might report false errors
CC3260MT.DLL
CC3260.DLL
Can I do something about that... or is that a normal warning. But still I
don't know why my app just stop working after I press the ok button on that
warning...
| Quote: | If you must track the memory yourself, then try the following (untested,
and
no thread-safety provided for simplicity):
|
What should I do to make it thread safe ? I know that we have some TThread
objects in our code and we absolutely do some new/delete inside of them... I
suppose that in that case the code sample wouldn't work... or should crash
or create some weird bihaviour!
Thanks,
Carlos
|
|
| Back to top |
|
 |
Remy Lebeau (TeamB) Guest
|
Posted: Wed Dec 14, 2005 11:09 pm Post subject: Re: Can I override the global new/delete in C++Builder 6 ? |
|
|
"Carlos" <cmejia (AT) trellianetworks (DOT) com> wrote
| Quote: | I also tried CodeGuard but that gives me some warning and my
app doesn't work after that. This is the only warning that I have:
Two different CRTLDLLs are loaded. CG might report false errors
CC3260MT.DLL
CC3260.DLL
|
Your project is linking to both the single-threaded and multi-threaded
versions of Borland's RTL at the same time. It should not be doing that in
the first place.
| Quote: | I don't know why my app just stop working after I press the ok
button on that warning...
|
Just saying "stop working" says nothing at all about the actual problem you
are having. You need to be more specific.
| Quote: | What should I do to make it thread safe ?
|
Wrap all access to the tracking list wth a synchronization object, such as a
critical section.
Gambit
|
|
| Back to top |
|
 |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
|