Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Macro to replace C++ operator new

People also ask

What does ## do in a macro?

The double-number-sign or token-pasting operator (##), which is sometimes called the merging or combining operator, is used in both object-like and function-like macros. It permits separate tokens to be joined into a single token, and therefore, can't be the first or last token in the macro definition.

What is macro substitution in C?

Macro substitution is a mechanism that provides a string substitution. It can be achieved through "#deifne". It is used to replace the first part with the second part of the macro definition, before the execution of the program. The first object may be a function type or an object.

What is __ Va_args __ in C?

__VA_ARGS__ is replaced by all of the arguments that match the ellipsis, including commas between them. The C Standard specifies that at least one argument must be passed to the ellipsis to ensure the macro doesn't resolve to an expression with a trailing comma.

What is __ function __ in C?

(C++11) The predefined identifier __func__ is implicitly defined as a string that contains the unqualified and unadorned name of the enclosing function. __func__ is mandated by the C++ standard and is not a Microsoft extension.


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


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

https://docs.microsoft.com/en-us/archive/blogs/calvin_hsia/overload-operator-new-to-detect-memory-leaks


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

  1. 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.


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'.


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).