Memory leak reported by Vis Studio CRT report that's before main starts

Started by
11 comments, last by ApochPiQ 12 years, 5 months ago
Hi guys.

I am cleaning up code and got my leaks down to only 2 16-byte leaks (64x build). Visual Studio's CRT memory leak detector gives me the allocation #'s, and I've had great luck cleaning up all the other leaks using the _CrtSetBreakAlloc() with this method. However, with these two leaks - it never hits the breakpoint and just starts up. So, right in my main, I do this:


int WINAPI wWinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow )
{
int* pi = new int();
}
which obviously leaks, but it reports that leak as allocation #261. However, the reported leaks are allocations #259 and 260 - which is before main starts. Looking at the dumped data, I had previously had 5 additional leaks of static global std:string objects that showed up allocated before they started. Since I had the dump of the memory and could read the strings, and was able to find them. But with these two, I can't get a line reference number and they appear to be holding pointer data (possibly?).

I'm using the
#define DBG_NEW new ( _NORMAL_BLOCK , __FILE__ , __LINE__ )
to retrieve file/line numbers in the output dump, but am not getting a report. My guess is because static allocations don't new()?

Is there some cool trick of CRT memory functionality that could report back where these are happening? Am I just not instrumenting the right file(s) to report the line number back (I have a lot of files in this solution) - or do static allocations like this not get line numbers?

Thanks in advance
Advertisement
Static objects (e.g. Foo object; at global scope in a file) will not allocate on the free-store and will not show up as leaks.

If those objects in turn make allocations, e.g. Foo's constructor calls new, then you will get stuff pre-main that shows up as leaks. Chief suspects here are singletons of any flavor.


In my experience, if you're not getting file/line information from the CRT debugger, one of three things is happening:
  1. You didn't instrument the right file (doing this can be hard in large projects, so I feel your pain there)
  2. There's some evil going on with other macros replacing new out from under you
  3. Someone is calling malloc() or something else which allocates via the CRT but doesn't do it via overloaded new


Best of luck; I've found these kinds of things hideously frustrating. If you're on a suitable version of Windows, you might try Application Verifier as well - I found it a little easier to get reliable callstacks etc. from when tracing leaks.

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]

Another option is Visual Leak Detector which goes to extreme lengths to replace every single instance of just about every memory allocation function you can think about with it's own definitions.

Another option is Visual Leak Detector which goes to extreme lengths to replace every single instance of just about every memory allocation function you can think about with it's own definitions.


So, tried Visual Leak Detector. Nice product by the way. It actually reports NO leaks (unless I add back in the one that I intentionally added).

Other ideas?
Best I can suggest is to start stripping bits of functionality out of the program until you can pinpoint what causes the reported leak. You might have quicker results starting from an empty project and adding in code until it shows up. Depending on your compiler version it may also be a known CRT implementation bug.

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]


Static objects (e.g. Foo object; at global scope in a file) will not allocate on the free-store and will not show up as leaks.

If those objects in turn make allocations, e.g. Foo's constructor calls new, then you will get stuff pre-main that shows up as leaks. Chief suspects here are singletons of any flavor.


In my experience, if you're not getting file/line information from the CRT debugger, one of three things is happening:
  1. You didn't instrument the right file (doing this can be hard in large projects, so I feel your pain there)
  2. There's some evil going on with other macros replacing new out from under you
  3. Someone is calling malloc() or something else which allocates via the CRT but doesn't do it via overloaded new


Best of luck; I've found these kinds of things hideously frustrating. If you're on a suitable version of Windows, you might try Application Verifier as well - I found it a little easier to get reliable callstacks etc. from when tracing leaks.


I'm pretty sure that #2 isn't happening. I wrote all the code originally and never used any new/malloc overloading. For the most part, I'm using standard C calls and not linking to other libraries. I might do more checking the #1 (there are a few more places I can add the code too), and maybe search for a malloc() - but I'm pretty sure I only use new.



Best I can suggest is to start stripping bits of functionality out of the program until you can pinpoint what causes the reported leak. You might have quicker results starting from an empty project and adding in code until it shows up. Depending on your compiler version it may also be a known CRT implementation bug.


Fascinatingly enough - I get the leak with this code:




int WINAPI wWinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow )
{
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); // can comment this line in/out with same results

_CrtDumpMemoryLeaks();
return 0;
}




This tells me it's happening before the *start* of the program. The size of the 'leak' depends on whether I'm doing x86 or x64 build. But in both cases Visual Leak Detector detects no leaks. I added one to make sure it was working and it reported that leak correctly.

x86 build


Detected memory leaks!
Dumping objects ->
{270} normal block at 0x0093E270, 8 bytes long.
Data: < E > C8 9B 45 01 00 00 00 00
{269} normal block at 0x0093E228, 8 bytes long.
Data: < E > A8 9B 45 01 00 00 00 00



x64 build


Detected memory leaks!
Dumping objects ->
{268} normal block at 0x0000000001E02A20, 16 bytes long.
Data: < ? > F0 A1 EB 3F 01 00 00 00 00 00 00 00 00 00 00 00
{267} normal block at 0x0000000001DFFF80, 16 bytes long.
Data: < ? > C0 A1 EB 3F 01 00 00 00 00 00 00 00 00 00 00 00
Object dump complete.


Furthermore, when I add back in the int* p=new int 'leak', it's address is usually within about +0x80 bytes of allocation 268 - which may imply it's getting allocated before my program starts...
Remove the explicit call to _CrtDumpMemoryLeaks(), and let it run at program exit (I forget the flag for that offhand, check MSDN). You won't get false-positives from the CRT that way.

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]


Remove the explicit call to _CrtDumpMemoryLeaks(), and let it run at program exit (I forget the flag for that offhand, check MSDN). You won't get false-positives from the CRT that way.


According to the microsoft docs, it's:
_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
However, I don't get a report at exit as the docs say I should... Hmmmm.

MORE data. I was checking the microsoft docs (http://msdn.microsof...y/x98tx3cf.aspx), and see that you ALSO need to #define _CRTDBG_MAP_ALLOC_NEW:

#define _CRTDBG_MAP_ALLOC
#define _CRTDBG_MAP_ALLOC_NEW
#include <stdlib.h>
#include <crtdbg.h>

#ifdef _DEBUG
#ifndef DBG_NEW
#define DBG_NEW new ( _NORMAL_BLOCK , __FILE__ , __LINE__ )
#define new DBG_NEW
#endif
#endif // _DEBUG


Adding THIS gives me my answer:

Detected memory leaks!
Dumping objects ->
c:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\include\crtdbg.h(1116) : {268} normal block at 0x0000000001E32A20, 16 bytes long.
Data: < ? > F0 A1 B5 3F 01 00 00 00 00 00 00 00 00 00 00 00
c:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\include\crtdbg.h(1116) : {267} normal block at 0x0000000001E2FF80, 16 bytes long.
Data: < ? > C0 A1 B5 3F 01 00 00 00 00 00 00 00 00 00 00 00


which is this code in crtdbg.h:

_Ret_bytecap_(_Size) inline void * __CRTDECL operator new(size_t _Size)
{ return ::operator new(_Size, _NORMAL_BLOCK, __FILE__, __LINE__); }


However, many people seem to report that crtdbg.h is sometimes blamed for an allocation when it's not if they have their config wrong, so I'm a little skeptical. Instead, what I did was set a memory checkpoint at the begin/end of my program. It reports that I'm not leaking any memory:


Deleting:
0 bytes in 0 Free Blocks.
0 bytes in 0 Normal Blocks.
12448 bytes in 7 CRT Blocks.
0 bytes in 0 Ignore Blocks.
0 bytes in 0 Client Blocks

.

One also sees this in their notes:
In some cases, _CrtDumpMemoryLeaks can give false indications of memory leaks. This might occur if you use a library that marks internal allocations as _NORMAL_BLOCKs instead of _CRT_BLOCKs or _CLIENT_BLOCKs. Older versions of the Standard Template Library, earlier than Visual Studio .NET, caused _CrtDumpMemoryLeaks to report such false positives, but this has been fixed in recent releases.[/quote]
I believe I DID see this before when I was using static std::string objects. They were reported as leaking (so maybe Microsoft HASN'T completely fixed it?). However, I can't find anymore static std:strings anywhere. Perhaps another stl container object that I'm using(?)
SOLUTION!!!

Wow. So, I started searching my code for static variables to see if I wasn't leaving one or two around - paying very close attention to any STL objects I was using (which was primarily std::vector and std::string). In searching for the keyword static, I found this:


class foo
{
private:
static bar m_mybar;
};



Looking at bar I see among the many members:

class bar
{
private:
std::string m_name;
std::string m_value;
};



BLAMO! I changed foo's m_myBar to:
static bar* m_myBar;
then create a global initializer to new/delete the m_myBar object at startup/shutdown. Leak goes away!

I double-verified this was the culprit by initializing m_name and m_value to actual strings, and sure enough, those strings are dumped by _CrtDumpMemoryLeaks();

The reason they were 8/16 bytes was because an 'empty' std::string operator contains a pointer and an int length. On x86, 8 = sizeof(int)+sizeof(void*) and 16 = sizeof(int)+sizeof(void*) on x64.

Long story short: *static* STL container objects are NOT fixed as the MS doc indicates - and DO show up as leaks using _CrtDumpMemoryLeaks(). Time to file a bug report.

Holy cow. And the crowd goes wild.

This topic is closed to new replies.

Advertisement