36

How can I track the memory allocations in C++, especially those done by new/delete. For an object, I can easily override the operator new, but I'm not sure how to globally override all allocations so they go through my custom new/delete. This should be not a big problem, but I'm not sure how this is supposed to be done (#define new MY_NEW?).

As soon as this works, I would assume it's enough to have a map somewhere of pointer/location of the allocation, so I can keep track of all allocations which are currently 'active' and - at the end of the application - check for allocations which have not been freed.

Well, this seems again like something that surely has been done several times at least, so any good library out there (preferably a portable one)?

Anteru
  • 18,283
  • 10
  • 72
  • 117
  • There is no generic off-shelf answer. Please provide more information about the OS and platform used. – kauppi Jan 13 '09 at 10:59
  • I would need a solution which works at least on both Linux and Windows, and preferably also on Mac OS. – Anteru Jan 13 '09 at 12:27

15 Answers15

30

I would recommend you to use valgrind for linux. It will catch not freed memory, among other bugs like writing to unallocated memory. Another option is mudflap, which tells you about not freed memory too. Use -fmudflap -lmudflap options with gcc, then start your program with MUDFLAP_OPTIONS=-print-leaks ./my_program.

Here's some very simple code. It's not suitable for sophisticated tracking, but intended to show you how you would do it in principle, if you were to implement it yourself. Something like this (left out stuff calling the registered new_handler and other details).

template<typename T>
struct track_alloc : std::allocator<T> {
    typedef typename std::allocator<T>::pointer pointer;
    typedef typename std::allocator<T>::size_type size_type;

    template<typename U>
    struct rebind {
        typedef track_alloc<U> other;
    };

    track_alloc() {}

    template<typename U>
    track_alloc(track_alloc<U> const& u)
        :std::allocator<T>(u) {}

    pointer allocate(size_type size, 
                     std::allocator<void>::const_pointer = 0) {
        void * p = std::malloc(size * sizeof(T));
        if(p == 0) {
            throw std::bad_alloc();
        }
        return static_cast<pointer>(p);
    }

    void deallocate(pointer p, size_type) {
        std::free(p);
    }
};

typedef std::map< void*, std::size_t, std::less<void*>, 
                  track_alloc< std::pair<void* const, std::size_t> > > track_type;

struct track_printer {
    track_type * track;
    track_printer(track_type * track):track(track) {}
    ~track_printer() {
        track_type::const_iterator it = track->begin();
        while(it != track->end()) {
            std::cerr << "TRACK: leaked at " << it->first << ", "
                      << it->second << " bytes\n";
            ++it;
        }
    }
};

track_type * get_map() {
    // don't use normal new to avoid infinite recursion.
    static track_type * track = new (std::malloc(sizeof *track)) 
        track_type;
    static track_printer printer(track);
    return track;
}

void * operator new(std::size_t size) throw(std::bad_alloc) {
    // we are required to return non-null
    void * mem = std::malloc(size == 0 ? 1 : size);
    if(mem == 0) {
        throw std::bad_alloc();
    }
    (*get_map())[mem] = size;
    return mem;
}

void operator delete(void * mem) throw() {
    if(get_map()->erase(mem) == 0) {
        // this indicates a serious bug
        std::cerr << "bug: memory at " 
                  << mem << " wasn't allocated by us\n";
    }
    std::free(mem);
}

int main() {
    std::string *s = new std::string;
        // will print something like: TRACK: leaked at 0x9564008, 4 bytes
}

We have to use our own allocator for our map, because the standard one will use our overridden operator new, which would result in an infinite recursion.

Make sure if you override operator new, you use the map to register your allocations. Deleting memory allocated by placement forms of new will use that delete operator too, so it can become tricky if some code you don't know has overloaded operator new not using your map, because operator delete will tell you that it wasn't allocated and use std::free to free the memory.

Also note, as Pax pointed out for his solution too, this will only show leaks that are caused by code using our own defined operator new/delete. So if you want to use them, put their declaration in a header, and include it in all files that should be watched.

Johannes Schaub - litb
  • 466,055
  • 116
  • 851
  • 1,175
  • 1
    Great post. I was very helped from your example to track and fix a memory leak in an embedded device :) – tkarls Aug 16 '12 at 08:53
  • 2
    Good example! One thing to note, this code is not thread safe, so in a multi-threaded environment (where `new` and `delete` would be called from multiple threads) you would have to protect access to the `track` map with a `std::mutex`. – gbmhunter Oct 13 '17 at 19:20
29

To be specific, use valgrind's massif tool. As opposed to memcheck, massif is not concerned with illegal use of memory, but tracking allocations over time. It does a good job of 'efficiently' measuring heap memory usage of a program. The best part is, you don't have to write any code. Try:

http://valgrind.org/docs/manual/ms-manual.html

Or if you are really impatient:

valgrind --tool=massif <executable> <args>
ms_print massif.out.<pid> | less

This will give you a graph of allocations over time, and back traces to where the big allocations ocurred. This tool is best run on Linux, I don't know if there is a Windows varient. It does work on OS X.

Good luck!

Necro
  • 501
  • 5
  • 9
11

You can use the code at http://www.flipcode.com/archives/How_To_Find_Memory_Leaks.shtml with the following modifications: the code as given only works if you have one big honkin' source file. I sorted this out for another question on SO (here).

For a start, don't change stdafx.h, make your modifications in your own files.

Make a separate header file mymemory.h and put your function prototypes in it, for example (note that this has no body):

inline void * __cdecl operator new(unsigned int size,
    const char *file, int line);

Also in that header, put the other prototypes for AddTrack(), DumpUnfreed(), etc., and the #defines, typedef and the extern statement:

extern AllocList *allocList;

Then, in a new mymemory.cpp (which also #include's mymemory.h), put the actual definition of allocList along with all the real functions (not just the prototypes) and add that file to your project.

Then, #include "mymemory.h" in every source file in which you need to track memory (probably all of them). Because there are no definitions in the header file, you won't get duplicates during the link and because the declarations are there, you won't get undefined references either.

Keep in mind that this won't track memory leaks in code that you don't compile (e.g., third-party libraries) but it should let you know about your own problems.

Community
  • 1
  • 1
paxdiablo
  • 772,407
  • 210
  • 1,477
  • 1,841
7

Well, you can re-implement the global operators new and delete to give you the functionality you want, but I'd advise against that unless this is the only way to track memory allocations, due to restrictions of your platform for example.

Memory debuggers are available for most of the common development platforms. Have a look at PurifyPlus for a commercial solution that works on Windows and various Unixes or valgrind for an open source one that works on Linux (and potentially other operating systems but I've only ever used it on Linux).

If you are intent on replacing the global operators, have a look at this article.

Timo Geusch
  • 23,267
  • 4
  • 48
  • 70
4

For our Windows platform C++ projects, I use VLD, Visual Leak Detector, which is almost too easy to implement that tracks and reports on memory leaks when your application exits - best of all its free and the source is available. The system can be setup to report in a number of ways (disk logger, IDE, XML etc) and has been invaluable for detecting leaks in Windows Services which are always a challenge to debug. So whilst you are looking for a portable solution, if you wish to roll your own you can of course view the source for guidance. Hope it helps.

To quote the site:

It's a very effective way to quickly diagnose, and fix, memory leaks in C/C++ applications.

http://dmoulding.googlepages.com/vld

Damien
  • 277
  • 1
  • 8
3

If you develop under Windows the free tool DebugDiag will help find memory and handle leaks.

You don't need to augument your program for DebugDiag to work.

http://www.microsoft.com/downloads/details.aspx?FamilyID=28BD5941-C458-46F1-B24D-F60151D875A3&displaylang=en

Although it is not the easiest or most intuitive program to use! Make sure you google for tutorials and instructions on how to use it.

Ashley Davis
  • 9,255
  • 7
  • 58
  • 79
3

On Linux, there's at least two traditional methods:

  • malloc() and free() (and other memory-related functions) are weak symbols, which means that you can simply reimplement them and your versions will be used. For an implementation example: see electric fence.
  • With the LD_PRELOAD environment variable, you can override symbols (both weak and strong) symbols in shared libraries with the symbols found in the libraries contained in the LD_PRELOAD environment variable. If you compile a shared library with malloc(), free() and friends, you're all set. Again, electric fence demonstrates this.

As such, you don't only catch new and delete, but also the C-style memory allocation functions. I haven't done this on windows yet, but I've seen methods to rewrite how DLLs are linked there too (although I recall they were kind of clumsy).

Note however that apart from the fact that these are interesting techniques, I'd recommend to use valgrind to do what you want above anything else.

user52875
  • 2,960
  • 19
  • 20
1

Not directly answering your question, but if you really just want to get a list of leaked heap-objects at the end of the program, you may well just run the program with valgrind.

For MS VS you may play with the Debug CRT Heap. Not as simple as valgrind, a bit too much to explain here, but may do what you want.

gimpf
  • 4,396
  • 26
  • 37
  • Yeah, I'm using these at the moment, but I'd like to switch the memory allocator (especially to track memory in various categories), so I need a custom solution here. – Anteru Jan 13 '09 at 10:24
1

If I need a tool I usually start from what my compiler/standard library provides.

  • If you use glibc you can use mtrace. It installs a global hook that logs every glibc memory allocation function (malloc, realloc, memalign, free, and everything implemented on top of them like new/delete)
  • If you use Microsoft CRT you can look at CRT Debug Heap Details. There are examples how to install debug version of memory allocation functions, getting heap statistics, finding memory leaks, etc.
Dmytro Voloshyn
  • 269
  • 4
  • 5
0

You can use add a header file (MemTracker.h) given in this link to your solution to track memory allocation/deallocation in C and C++. It shows if you have a memory leak and which line of code is responsible for it.

hnl
  • 1
  • 2
0

If you mean to do this as a programming exercise, it might give you much more insight to write your own smart pointer class(es) instead, and consistently use them throughout this one project (or module of a project).

0

If you're developing under linux, one of the best tools for this (eg. detecting memory leaks, tracking allocations done at certain places of code) is valgrind, particularly its massif tool. The only disadvantage is that the program runs slower (or much slower) so it's only useful for debugging.

jpalecek
  • 44,865
  • 7
  • 95
  • 139
0

I noticed a lot of the other answers focus on what tools you can use. I've used some of them, and they help a lot.

But as a programming excercise, and seeing that you work with c++, you will need to override the global new and delete, as well as malloc, free and realloc. You'd think that only overriding new and delete would be enough, but the std::string and other classes are likely to use malloc and especially realloc.

Then, once you have this in place, you can start adding headers to check for memory overwrites, record stack traces per allocation and so on.

All in all, I'd recommend that you go with one of the tools mentioned here, but it could be fun to write your own system.

Jørn Jensen
  • 898
  • 1
  • 10
  • 16
  • I seriously doubt that a std:;string will use realloc, as it has to use the provided allocator, which does not support realloc. – Anteru Jan 13 '09 at 12:43
0

Check this tiny handy code , now instead of new use NEW and track all allocations in the NewHelper constructor :

#include <iostream>

class NewHelper
{
   private :
    void* addr = nullptr;
       public :
       NewHelper(void * addr_)
       {
          addr = addr_;
          std::cout<<addr<<std::endl;
       }
       template <class T>
       operator T ()
       {
           return (T)addr;
       }
};
#define NEW (NewHelper)(void*)new
int main()
{
  int * i = NEW int(0);
 return 0;
}
Dhia Hassen
  • 404
  • 3
  • 15
  • This will not track allocations from any library code. Furthermore, your `(void*)` sacrifices the type-safety we get with `new`. – Lightness Races in Orbit Feb 26 '18 at 16:39
  • the compiler will reconvert type using NewHelper::operator T , anyways , i have coded a fullc/c++ memory tracer that traces every single allocation in you files and the stdlib files .... , i can sale it if anyone interested , features : - Log stacktrace for all allocations that are never freed :- Log stacktrace for all allocation free more than once -Stacktrace for allocations invalide free() ... - showing stacktrace for all allocation happned in attributes contructor's when the parent object is allocated but never deleted ( constructor not called ) – Dhia Hassen Feb 27 '18 at 13:50
0

It is not cheap, but I used to find in my C++ days that purify was the best tool for debugging leaks and other memory issues (Same it is now owned by IBM, so surport went down hill). Bounds Checker was liked by some people, but did not work well for the software I was developing.

Ian Ringrose
  • 49,271
  • 50
  • 203
  • 302