Reputation: 43619
Is it possible to create macros to replace all forms of operator new
with overloads that include additional args...say __FILE__
and __LINE__
?
The trouble appears to be that operator new
can either be coded with or without parentheses, therefore:
object-like macros:
#define new new(__FILE__, __LINE__)
will replace declarations like:
A* a = new A();
and function-like macros:
#define new(A) new (A, __FILE__, __LINE__)
will replace declarations like:
A* a = new(std::nothrow) A();
Unfortunately it's an error to attempt to declare two macros with the same identifier, even if they are of different types, so the following fails:
#define new new(__FILE__, __LINE__)
#define new(A) new (A, __FILE__, __LINE__) // Error: "new" already defined
Since I'm using g++ I was hopeful that employing their syntax of variadic macros would yield success, but unfortunately not. The following:
#define new(...) new(__FILE__, __LINE__, ## __VA_ARGS__)
only matches new(xyx) A()
, not new A()
.
I know that essays have been written about why it is impossible, but I feel like I'm so close that there must be a way. Is there anything obvious that I'm missing?
Upvotes: 29
Views: 19493
Reputation: 755161
You should check out this excellent blog entry by my coworker Calvin. We had a situation recently where we wanted to enable this type of fix in order to associate memory leaks with the line that allocated them in diagnostic/debug builds. It's an interesting trick
Upvotes: 8
Reputation:
Here is what I use:
In new.cpp
const char* __file__ = "unknown";
size_t __line__ = 0;
void* operator new(size_t size) {
void *ptr = malloc(size);
record_alloc(ptr,__file__,__line__);
__file__ = "unknown";
__line__ = 0;
return ptr;
}
void delete(void *ptr)
{
unrecord_alloc(ptr);
free(ptr);
}
For compactness, I'm leaving out the other definitions of new and delete. "record_alloc" and "unrecord_alloc" are functions that maintain a linked list of structure containing ptr, line, and file).
in new.hpp
extern const char* __file__;
extern size_t __line__;
#define new (__file__=__FILE__,__line__=__LINE__) && 0 ? NULL : new
For g++, "new" is expanded only once. The key is the "&& 0" which makes it false and causes the real new to be used. For example,
char *str = new char[100];
is expanded by the preprocessor to
char *str = (__file__="somefile.c",__line__=some_number) && 0 ? NULL : new char [100];
Thus file and line number are recorded and your custom new function is called.
This works for any form of new -- as long as there is a corresponding form in new.cpp
Upvotes: 28
Reputation: 20237
What you could do is to overload the operator new and get the stack trace there (platform specific) and use the stack information to deduce from where new was called.
Upvotes: 0
Reputation: 111210
3.7.4 Dynamic storage duration
2 The library provides default definitions for the global allocation and deallocation functions. Some global allocation and deallocation functions are replaceable (18.5.1). A C++ program shall provide at most one definition of a replaceable allocation or deallocation function. Any such function definition replaces the default version provided in the library (17.6.4.6) [...]
17.6.4.6 Replacement functions
A C++ program may provide the definition for any of eight dynamic memory allocation function signatures declared in header (3.7.4, Clause 18):
- operator new(std::size_t)
- operator new(std::size_t, const std::nothrow_t&)
- operator new[](std::size_t)
- operator new[](std::size_t, const std::nothrow_t&)
- operator delete(void*)
- operator delete(void*, const std::nothrow_t&)
- operator delete[](void*)
- operator delete[](void*, const std::nothrow_t&)
Hope this clarifies what is a legal overload and what isn't.
This may be of interest to a few here:
#define delete cout << "delete called at: " << __LINE__ << " of " << __FILE__ << endl, delete
using namespace std;
void *operator new(size_t size, ostream& o, char *f, unsigned l) {
o << "new called at: " << l << " of " << f << endl;
return ::new char[size];
}
int main() {
int *a = new(cout, __FILE__, __LINE__) int;
delete a;
}
Caveat Lector: What I do here is a Bad Thing (TM) to do -- overloading new/delete globally.
Upvotes: 4
Reputation: 292
I found the following library "nvwa" very useful for tracking down new/delete memory leaks - have a look at the file "debug_new" for examples, or just use it 'as is'.
Upvotes: 2
Reputation: 7445
You don't say what compiler you are using, but at least with GCC, you can override new and log the caller address, then later translate that to file/line information with addr2line (or use the BFD library to do that immediately).
Upvotes: 1
Reputation: 83051
No, there's no way.
You could do this in the bad old days of malloc()/free()
but not for new
.
You can replace the memory allocator by globally overriding the new
operator, but you cannot inject the special variables you're talking about.
Upvotes: -2