Skip to content

Dr. Memory – Valgrind For Windows?

May 19, 2012

Linux programmers swear by Valgrind. What does Valgrind do? Well, it’s a memory leak detector. Take a look at this code:

#include <iostream>
#include <cstring>
using namespace std;

char * f() {
    char * p = new char[100];
    strcpy( p, "foobar" );
    return p;
}

int main() {
    for( int i = 0; i < 10; i++ ) {
        cout << f() << endl;
    }
}

There’s a pretty obvious memory leak here. Can you spot it? Valgrind can. I compiled this code with GCC on a Linux box and then ran Valgrind on it. I got this output (note – I have edited the output of all tools mentioned here in the interests of readability on a web page):

1,000 bytes in 10 blocks are lost in loss record 1 of 1
at 0x4025F53: operator new[](unsigned int) 
by 0x80486A5: f() (vg.cpp:6)
by 0x80486E0: main (vg.cpp:13)
 
LEAK SUMMARY:
    definitely lost: 1,000 bytes in 10 blocks
    indirectly lost: 0 bytes in 0 blocks
    possibly lost: 0 bytes in 0 blocks
    still reachable: 0 bytes in 0 blocks
    suppressed: 0 bytes in 0 blocks

Valgrind is telling us there is a leak at line 13 in main, where the function f() is called. Of course, this leak is pretty obvious, but Valgrind is good at pinpointing much less obvious leaks. Such tools are valuable, particularly when programming in languages where all memory management must be done manually. C++, by the way, is not such a language. If I had written the program thus there would be no leak:

#include <iostream>
#include <string>
using namespace std;

string f() {
    string p = "foobar";
    return p;
}

int main() {
    for( int i = 0; i < 10; i++ ) {
        cout << f() << endl;
    }
}

However, even C++ programmers stoop to manual memory management on occasion, so tools like Valgrind are valuable to them too.

Unfortunately, Valgrind is tied to UNIX-like platforms such as Linux and the Mac. Is there an equivalent in the Windows world? For a long time there were really only commercial, closed source products available, but now there are also some FOSS contenders. The one I’m going to look at here is Dr. Memory.

Dr. Memory is very similar in operation to Valgrind – you compile your program as normal, (ideally with debug information), and then run it via Dr. Memory. I compiled the above program on Windows using TDM MinGW like this:

g++ -g vg.cpp -o vg.exe

and then ran it through Dr. Memory like this:

drmemory .\vg.exe

which produced this output:

Error #1: LEAK 100 direct bytes + 0 indirect bytes
# 0 operator new()
# 1 operator new()
# 2 operator new[]()
# 3 main     [c:\users\neilb\home\temp/vg.cpp:13]
ERRORS FOUND:
      0 unique,     0 total unaddressable access(es)
      0 unique,     0 total uninitialized access(es)
      0 unique,     0 total invalid heap argument(s)
      0 unique,     0 total warning(s)
      1 unique,    10 total, 1000 byte(s) of leak(s)
      0 unique,     0 total, 0 byte(s) of possible leak(s)

You can see that Dr. Memory has identified the line the leak occurred at (line 13), but not the function call that returned the leaky pointer. My general impression is that Dr. Memory provides slightly less information than Valgrind, but I have not tried all (or even most) of the tool’s many, many  command-line options, so I could be mistaken.

So, Dr. Memory works with a toy program, but how does it fare in the real world? I have a FOSS project csvtest which generates random but meaningful data for testing and other purposes. A user had just reported that csvtest was running out of memory when he tried to use it to generated 10 million CSV records. The way he was using the utility meant that data would be cached in memory, but it looked as if more should be cached than actually was. So, was there a memory leak?  as someone who’s mantra is and always has been "The best way to avoid memory leaks is not to write them", I was pretty sure there wasn’t, but what would Dr. Memory think?

Actually, it gave me a clean bill of health. Running it on csvtest and the script that caused the memory exhaustion problem, I got:

NO ERRORS FOUND:
    0 unique,     0 total unaddressable access(es)
    0 unique,     0 total uninitialized access(es)
    0 unique,     0 total invalid heap argument(s)
    0 unique,     0 total warning(s)
    0 unique,     0 total, 0 byte(s) of leak(s)
    0 unique,     0 total, 0 byte(s) of possible leak(s)

But how would it fare if there actually was a memory leak in a reasonably ssized application. I introduced a memory leak in one of my base class destructors, changing:

CompositeDataSource :: ~CompositeDataSource() {
    ALib::FreeClear( mSources );
}

to:

CompositeDataSource :: ~CompositeDataSource() {
}

The FreeAndClear function is a utility template function that calls delete on all pointers in a C++ container, and then clears it – this is old code which doesn’t use smart pointers in containers.

With that change, I got the following output:

Error #1: LEAK 32 direct bytes + 16 indirect bytes
# 0 operator new()
# 1 operator new()
# 2 DMK::DSCounter::FromXML()  [dmk_counter.cpp:97]
# 3 DMK::RegisterDS<>::CreateDS() [dmk_tagdict.h:98]
# 4 DMK::TagDictionary::CreateDS() [dmk_tagdict.cpp:74]
# 5 DMK::CompositeDataSource::AddChildSources()[dmk_source.cpp:106]
# 6 DMK::DSUnique::FromXML() [dmk_unique.cpp:142]
# 7 DMK::RegisterDS<>::CreateDS()[inc/dmk_tagdict.h:98]
# 8 DMK::TagDictionary::CreateDS() [dmk_tagdict.cpp:74]
# 9 DMK::Generator::AddSources() [dmk_model.cpp:148]
#10 DMK::GeneratorTag::FromXML() [dmk_generator.cpp:154]
#11 DMK::RegisterGen<>::CreateGen() [dmk_tagdict.h:74]

ERRORS FOUND:
    0 unique,     0 total unaddressable access(es)
    0 unique,     0 total uninitialized access(es)
    0 unique,     0 total invalid heap argument(s)
    0 unique,     0 total warning(s)
    1 unique,     1 total, 48 byte(s) of leak(s)
    0 unique,     0 total, 0 byte(s) of possible leak(s)

In the above, DMK is the csvtest namespace (for historic reasons). You can see that the destructor is not listed as being the cause of the leak, and if I had known where the leak was, the information provided would have helped tracking it down, but would hardly have pinpointed it. I cannot provide a comparison with Valgrind here, as csvtest is currently a Windows-only application.

In conclusion, I see tools like Dr. Memory more as a form or reassurance than as bug-finders. I will continue to rely on "The best way to avoid memory leaks is not to write them" as a guiding principle – use smart pointers, use RAII – rather than depending on tools like Dr. Memory. However, it is pretty good at doing what it does, and I suggest you take a look at it, if you program on Windows. I should mention that it works with VC++ as well as GCC, although I haven’t tested it with that compiler.

Advertisements

From → c++, foss, freeware, windows

Leave a Comment

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: