Wednesday, December 5, 2007

Memory Leak Detection with Visual Studio 2008

Brad Fish told me that Visual Studio 2008 was definitely worth the upgrade (especially on Vista), so I took the plunge. For a C++ programmer, I don't think it was as cool of a jump as 2003 to 2005 and not even close to the HUGE jump from 6 to 2003 (I skipped 2002), but it's still been a very nice upgrade.

But anyway, I have been working on making my own little SQLite3 C++ wrapper, because I just didn't like the ones that I could find out there. While doing that I also decided that it would be easiest to use a smart pointer to manage all of the pointers to SQLite stuff. I had played around with the Boost shared_ptr, but once again I just didn't like some of the syntax (mostly the custom deleter being in the constructor rather than a template parameter), so I just dusted off some old shared pointer code of my own and added a custom deleter to it.

It all seemed to be working just fine, but I just wanted to make sure that I was cleaning everything up properly and then I remembered that the current versions of Visual Studio don't dump out the memory leaks like Visual Studio 6 used to do by default. So I started some Googling and found this page. It says that it doesn't work with Express Edition, but it must be a typo or something, because it works (just not quite as described). Basically, here's what I was able to figure out from playing around with it. All you need to do is:
1) Add the header crtdbg.h
2) Call _CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );

You can find out more about _CrtSetDbgFlag() here, but calling it this way this will turn on the printing out of the memory leaks when your program exits (only when _DEBUG is defined).

After creating some artifical memory leaks, I noticed that it didn't dump the line that the memory was allocated on like it used to. So I did a little scavenging through the crtdbg.h header file and I found a conditional compile that would enable the printing of the allocation line. I added the two conditions to my code:
#define _CRTDBG_MAP_ALLOC
#define _CRTDBG_MAP_ALLOC_NEW
But unfortunately, this is just a mechanism to allow old code to still compile, because now it just prints the line in the crtdbg.h file. This is obviously useless (something that's I just noticed is also pointed out in the comments of the crtdbg.h file), so I guess this is the first thing I've found in the newer versions of Visual Studio that just aren't up to par with Visual Studio 6 (I know I didn't think I'd ever say/hear/read that either).

I guess that I should just be happy that I was able to get my memory leak dump again, but I'm still wondering why that's not on by default.

8 comments:

Brad said...

Hey, when you're done with your SQLite wrapper, I'd be interested in seeing it! I've got one of my own, but it might be fun to compare or even collaborate.

It is rather inconvenient that the custom deleter for boost::shared_ptr has to be passed in the constructor, although I realize why they designed it that way (there's a blurb on the documentation page about the benefits of the design, such as source and binary compatibility independent of the deleter used).

Dave Johansen said...

I've been thinking about creating a Google Code project or something to store all of the code, but I've just been waiting until I go and actually clean everything up to the point that I'm not ashamed of it. So I'll let you know when I finally do something like that, because I'd love to get some feedback on it and it would be even cooler if it actually became something useful to someone other than me.

Ya, I read through a lot of the comments they wrote about their shared pointer and I just don't buy into a lot of the reasoning they use. I hope that it's just that I'm being a little too narrow minded and they're really creating a wonderful and broadly applicable library, but sometimes I wonder.

Also, the big reason that I steered away from using the boost shared_ptr is because Boost is a monstrosity. You can't just grab a single header or two and use them in a program, and it's usually not worth including some enormous amount of code when you just need one tiny piece of the provided functionality. Plus, above all, I wanted to maintain the whole SQLite idea of small, simple, and self-contained, so someone could drop this wrapper in and start using it immediately just like with SQLite.

Brad said...

Yeah, it took me a long time to start using boost because I didn't want a huge dependency in my code. The thing that changed my mind was the fact that boost::shared_ptr, boost::weak_ptr, and boost::enable_shared_from_this are all in TR1 for the next C++ standard (although of course they will all be in std::, not boost::). I figured I could either hack my own smart pointers, or I could go with something that is mature, tested, and standard (or which will soon be standard).

I have yet to use any other part of boost, mainly because I want to stick with stuff that is being standardized. Also, I discovered the bcp utility which allows you to extract only the parts of boost that you need. In the case of boost::shared_ptr and friends, bcp extracts only the few necessary header files which you can start using immediately without any compilation (there are no implementation files, just headers, for the smart pointer stuff).

There are a few other deal sealing features that have made me a convert to shared_ptr. One of them is the fact that the reference counting is done with atomic operations, so it is entirely safe to use shared_ptrs in multi-threaded applications without needing to worry about synchronizing the pointers themselves (of course you still need to synchronize operations to the pointed object if necessary).

Lastly, I do understand their reasoning behind not making the deleter a template parameter, even though it can be an inconvenience sometimes. It allows other code to be ignorant of the deleter, which really is the way it should be. Code that uses the smart pointer shouldn't need to be aware of the deallocation stuff.

Dave Johansen said...

I definitely agree that the fact that it's in TR1 is pretty sweet, and I'm really glad that Microsoft seems to be doing such a good job of supporting it. But my biggest questions is why is the namespace "std::tr1::"? What's wrong with just putting it in "std::"? I'm sure that was a fairly highly debated topic and no matter what they picked someone was gonna be pissed.

I'll have to check out that bcp extraction tool, because that would make using boost in small projects SO much easier.

I think that there's a part of me that doesn't like the boost::shared_ptr because I don't understand how they pulled off the custom deleter. So I'm definitely gonna have to take some time and look through that code to see how they did that, because that's some coding magic that I haven't seen yet.

Brad said...

Yeah, hopefully Microsoft's TR1 support will be good when it is finally released in VC++ 2008 Pro+ next year. I'm mostly worried about when they are going to make it available in the Express edition, which won't be until much later.

I think the reason it's going into std::tr1:: instead of std:: is that Microsoft is releasing it before it officially gets standardized. My understanding is that everything will go into std:: once the C++0x is finally completed. I'm pretty gcc also puts their advance versions of TR1 stuff into std::tr1:: as well.

I've looked through the shared_ptr code, and it's actually not crazy at all. It's just done via a virtual dispose method on a non-template base class which is overriden in a template subclass for the given pointer and deleter types. It's pretty elegant, although there's basically two additional objects created on the heap for every object being pointed to by a shared_ptr (the reference count and the deleter wrapper) which I'm not crazy about. But, I don't think there's really any other way to accomplish this and I think the benefits outweigh the drawbacks.

Dave Johansen said...

That's lame that they won't be available for Express Edition, but what are you going to do about it.

I had also assumed that TR1 had already been standardized, so it definitely makes sense to put it in std::tr1 since it's still open to changes.

I just checked out the shared_ptr code and it's just like you said. It's actually a pretty smooth idea, but I'm still hesitant to believe that it was the best solution. I guess from a general library type of perspective it's a good option, but it just feels a little too Java-ish to me (sacrificing memory and CPU time in all cases just to make a specific use case a little simpler). I guess the problem is that I just can't get over the fact that you could get the source compatibility using a typedef. Yes, you would lose binary compatibility that way and I guess not having to recompile client code was really important to them for some reason. But like I said before, you're never going to make everyone happy.

Cameron Figgins said...

The one and only company that can resolve your problem in no time is Absolute Maintenance and Consulting. They are experts in terms of Leak detection los angelesles. They have the most updated technological devices to determine the leaks that might be lurking in the corners, ready to make havoc in your home. Their thermal scan will do X-ray and will find the accurate place where the leak started. They have highly trained technicians to fix any intrusion or water leak because they can waterproof your home to ensure your family's safety. After a hard day's work at the office, you can go home and relax knowing that AMC has solved the problem.

roney shaheen said...
This comment has been removed by a blog administrator.