How to find a memory leak in C++

梦想的初衷 提交于 2019-12-03 00:25:51

One way is to insert file name and line number strings (via pointer) of the module allocating memory into the allocated block of data. The file and line number is handled by using the C++ standard "__FILE__" and "__LINE__" macros. When the memory is de-allocated, that information is removed.

One of our systems has this feature and we call it a "memory hog report". So anytime from our CLI we can print out all the allocated memory along with a big list of information of who has allocated memory. This list is sorted by which code module has the most memory allocated. Many times we'll monitor memory usage this way over time, and eventually the memory hog (leak) will bubble up to the top of the list.

If you're running Linux, I suggest trying Valgrind.

Skizz

There are several forms of operator new:

void *operator new (size_t);
void *operator new [] (size_t);
void *operator new (size_t, void *);
void *operator new [] (size_t, void *);
void *operator new (size_t, /* parameters of your choosing! */);
void *operator new [] (size_t, /* parameters of your choosing! */);

All the above can exist at both global and class scope. For each operator new, there is an equivalent operator delete. You need to make sure you are adding logging to all versions of the operator, if that is the way you want to do it.

Ideally, you would want the system to behave the same regardless of whether the memory logging is present or not. For example, the MS VC run time library allocates more memory in debug than in release because it prefixes the memory allocation with a bigger information block and adds guard blocks to the start and end of the allocation. The best solution is to keep all the memory logging information is a separate chunk or memory and use a map to track the memory. This can also be used to verify that the memory passed to delete is valid.

new
  allocate memory
  add entry to logging table

delete
  check address exists in logging table
  free memory

However, you're writing embedded software where, usually, memory is a limited resource. It is usually preferable on these systems to avoid dynamic memory allocation for several reasons:

  1. You know how much memory there is so you know in advance how many objects you can allocate. Allocation should never return null as that is usually terminal with no easy way of getting back to a healthy system.
  2. Allocating and freeing memory leads to fragmentation. The number of objects you can allocate will decrease over time. You could write a memory compactor to move allocated objects around to free up bigger chunks of memory but that will affect performance. As in point 1, once you get a null, things get tricky.

So, when doing embedded work, you usually know up front how much memory can be allocated to various objects and, knowing this, you can write more efficient memory managers for each object type that can take appropriate action when memory runs out - discarding old items, crashing, etc.

EDIT

If you want to know what called the memory allocation, the best thing to do is use a macro (I know, macros are generally bad):

#define NEW new (__FILE__, __LINE__, __FUNCTION__)

and define an operator new:

void *operator new (size_t size, char *file, int line, char *function)
{
   // log the allocation somewhere, no need to strcpy file or function, just save the 
   // pointer values
   return malloc (size);
}

and use it like this:

SomeObject *obj = NEW SomeObject (parameters);

You compiler might not have the __FUNCTION__ preprocessor definition so you can safely omit it.

http://www.linuxjournal.com/article/6059

Actually from my experience it always better to create memory pools for embedded systems and use custom allocator/de-allocator. We can easily identify the leaks. For example, we had a simple custom memory manager for vxworks, where we store the task id, timestamp in the allocated mem block.

overloading new and delete should work if you pay close attention.

Maybe you can show us what isn't working about that approach?

I'm not an embedded environment expert, so the only advise I can give is to test as much code as you can on your development machine using your favorite free or proprietary tools. Tools for a particular embedded platform may also exist and you can use them for final testing. But most powerful tools are for desktops.

On desktop environment I like the job DevPartner Studio does. This is for Windows and proprietary. There're free tools available for Linux but I don't have much expirience with them. As an example there's EFence


If you override constructor and destructor of your classes, you can print to the screen or a log file. Doing this will give you an idea of when things are being created, what is being created, as well as the same information for deletion.

For easy browsing, you can add a temporary global variable, "INSTANCE_ID", and print this (and increment) on every constructor/destructor call. Then you can browse by ID, and it should make goings a little easier.

The way we did it with our C 3D toolkit was to create custom new/malloc and delete macros that logged each allocation and deallocation to a file. We had to ensure that all the code called our macros of course. The writing to the log file was controlled by a run time flag and only happened under debug so we didn't have to recompile.

Once the run was complete a post-processor ran over the file matching allocations to deallocations and reported any unmatched allocations.

It had a performance hit, but we only needed to do it once it a while.

Is there a real need to roll your own memory leak detection?

Assuming you can't use dynamic memory checkers, like the open-source valgrind tool on Linux, static analysis tools like the commercial products Coverity Prevent and Klocwork Insight may be of use. I've used all three, and have had very good results with all of them.

Lots of good answers.

I would just point out that if the program is one that, like a small command-line utility, runs for a short period of time and then releases all its memory back to the OS, memory leaks probably do no harm.

You can use a third party tool to do this.

You can detect leaks within your own class structures by adding memory counters in your New and Delete calls to increment/decrement the memory counters, and print out a report at your application close. However, this won't detect memory leaks for memory allocated outside your class system - a third party tool can do this though.

Can you describe what is "not working" with your log methods?

Do you not get the expected logs? or, are they showing everything is fine but you still have leaks?

How have you confirmed that this is definitely a leak and not some other type of corruption?

One way to check your overloading is correct: Instantiate a counter object per class, increment this in the new and decrement it in the delete of the class. If you have a growing count, you have a leak. Now, you would expect your log lines to be coinciding with the increment and decrement points.

Not specifically for embedded development, but we used to use BoundsChecker for that.

Use smart pointers and never think about it again, there's loads of official types around but are pretty easy to roll your own too:

class SmartCoMem
{
public:
SmartCoMem() : m_size( 0 ), m_ptr64( 0 ) {
}

~SmartCoMem() {
    if( m_size )
        CoTaskMemFree((LPVOID)m_ptr64);
}

void copyin( LPCTSTR  in, const unsigned short size )
{
    LPVOID ptr;
    ptr = CoTaskMemRealloc( (LPVOID)m_ptr64, size );
    if( ptr == NULL )
        throw std::exception( "SmartCoMem: CoTaskMemAlloc Failed" );
    else
    {
        m_size = size;
        m_ptr64 = (__int64)ptr;
        memcpy( (LPVOID)m_ptr64, in, size );
    }
}

std::string copyout( ) {
    std::string out( (LPCSTR)m_ptr64, m_size );
    return out;
}

__int64* ptr() {
    return &m_ptr64;
}

unsigned short size() {
    return m_size;
}

unsigned short* sizePtr() {
    return &m_size;
}

bool loaded() {
    return m_size > 0;
}

private:
    //don't allow copying as this is a wrapper around raw memory
    SmartCoMem (const SmartCoMem &);
    SmartCoMem & operator = (const SmartCoMem &);

__int64 m_ptr64;
unsigned short m_size;

};

There's no encapsulation in this example due to the API I was working with but still better than working with completely raw pointers.

For testing like this, try compiling your embedded code natively for Linux (or whatever OS you use), and use the a well-established tool like Valgrind to test for memory leaks. It can be a challenge to do this, but you just need to replace any code that directly accesses hardware with some code that simulates something suitable for your testing.

I've found that using SWIG to convert my embedded code into a linux-native library and running the code from a Python script is really effective. You can use all of the tools that are available for non-embedded projects, and test all of your code except the hardware drivers.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!