Sign in to follow this  
Max_Payne

Memory Management: Error in VS2003 headers?

Recommended Posts

Max_Payne    757
Someone earlier proposed that I use the _CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF ); call to make the VS2003 debugger automatically track memory leaks. However, this generates memory leak reports that tell you nothing about where the data was allocated. Trying to find a way to make this output more useful, I ran accross this MSDN article. Apparently, you can make it track your memory leaks without problems. So I did what is suggested... But it didn't work as planned... This is the output I get for my test memory leak: Detected memory leaks! Dumping objects -> e:\visual studio .net 2003\vc7\include\crtdbg.h(689) : {158} normal block at 0x007C7288, 4 bytes long. Data: < @> 00 00 A0 40 Object dump complete. The program '[1344] engine.exe: Native' has exited with code 0 (0x0). I decided to go check what was at line 689 of crtdbg.h, and had a bad surprise: inline void * __cdecl operator new(size_t s) { return ::operator new(s, _NORMAL_BLOCK, __FILE__, __LINE__); } This looks like a dumb mistake to me. It looks like someone tried to convert a macro into an inline function directly, but was not careful enough. Obviously, the file and line are *always* going to tell me that the allocation is coming from crtdbg.h... I am now trying to decide if I should make my own macro to fix this lameness...

Share this post


Link to post
Share on other sites
Verg    450
What's wrong with setting a breakpoint at that line? Are there that many placement new's in your project?

Alternatively, you could implement a global placement new yourself.

Paul Nettle's memory manager would probably help you find the problem.

www.fluidstudios.com

Share this post


Link to post
Share on other sites
Max_Payne    757
The problem is that it does not behave as the MSDN article suggest. As for other memory managers, they would need to be able to handle memory allocations accross DLL boundaries.

Share this post


Link to post
Share on other sites
Grain    500
I had the exact same problem, and after downloading several memory leak loggers that didn’t work, I cobbled this together from code I got of the net and studying how the MSDN method is supposed to work. I'm using VS2003 my self so it should be compatible for you.


//MyNew.h
#ifndef _MY_NEW_
#define _MY_NEW_
#include <windows.h>
#include <list>
using namespace std;

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

inline void __cdecl operator delete(void *p);


void AddTrack(DWORD addr, DWORD asize, const char *fname, DWORD lnum);
void RemoveTrack(DWORD addr);
void DumpUnfreed();



inline void * __cdecl operator new(unsigned int size,const char *file, int line)
{
void *ptr = (void *)malloc(size);
AddTrack((DWORD)ptr, size, file, line);
return(ptr);
}
inline void __cdecl operator delete(void *p)
{
RemoveTrack((DWORD)p);
free(p);
}


#ifdef _DEBUG
#define DEBUG_NEW new(__FILE__, __LINE__)
#else
#define DEBUG_NEW new
#endif
#define new DEBUG_NEW

class ALLOC_INFO {
public:
DWORD address;
DWORD size;
char file[128];
DWORD line;

ALLOC_INFO()
{
address = 0;
size = 0;
line = 0;
file[0] = 0;
}
bool operator ==(const ALLOC_INFO& Rh)
{
if(address != Rh.address) return false;
if(size != Rh.size) return false;
if(strcmp(file,Rh.file)) return false;
if(address != Rh.address) return false;

return true;
}
} ;


typedef list<ALLOC_INFO> AllocList;


#endif






//Mynew.cpp
#ifdef _DEBUG
#include "MYnew.h"

static AllocList allocList;

void AddTrack(DWORD addr, DWORD asize, const char *fname, DWORD lnum)
{
ALLOC_INFO info;

/* if(!allocList) {
allocList = new(AllocList);
}*/


//info = new(ALLOC_INFO);
info.address = addr;
strncpy(info.file, fname, 128);
info.line = lnum;
info.size = asize;
allocList.insert(allocList.begin(), info);
};

void RemoveTrack(DWORD addr)
{
AllocList::iterator i;

if(!allocList.size())
return;
for(i = allocList.begin(); i != allocList.end(); i++)
{
if((*i).address == addr)
{
allocList.erase(i);
break;
}
}
};

void DumpUnfreed()
{
AllocList::iterator i;
DWORD totalSize = 0;
char buf[1024];

if(!allocList.size())
return;

for(i = allocList.begin(); i != allocList.end(); i++) {
sprintf(buf, "%-50s:\t\tLINE %d,\t\tADDRESS %d\t%d unfreed\n",
(*i).file, (*i).line, (*i).address, (*i).size);
OutputDebugString(buf);
totalSize += (*i).size;
}
sprintf(buf, "-----------------------------------------------------------\n");
OutputDebugString(buf);
sprintf(buf, "Total Unfreed: %d bytes\n", totalSize);
OutputDebugString(buf);
};
#endif







//Put this at the beginning of every file that calls new or delete
#ifdef _DEBUG
#include "MYnew.h"
#endif

//Put this where your program exits.
#ifdef _DEBUG
DumpUnfreed();
#endif


Share this post


Link to post
Share on other sites
Quote:
Original post by Max_Payne
I decided to go check what was at line 689 of crtdbg.h, and had a bad surprise:


inline void * __cdecl operator new(size_t s)
{ return ::operator new(s, _NORMAL_BLOCK, __FILE__, __LINE__); }


This looks like a dumb mistake to me. It looks like someone tried to convert a macro into an inline function directly, but was not careful enough. Obviously, the file and line are *always* going to tell me that the allocation is coming from crtdbg.h...

I am now trying to decide if I should make my own macro to fix this lameness...


Did you try to add

#ifdef _DEBUG
#define new DEBUG_NEW
#endif // _DEBUG


(You know, these lines we always delete when they are present or never add if they are not [wink])

After you included all your headers? This way, you are supposed to use the correct new() operator (the crtdbg.h one is provided by default if you don't use the DEBUG_NEW macro).

Regards,

Share this post


Link to post
Share on other sites
Max_Payne    757
I haven't seen any "DEBUG_NEW" definition in my brief look in there. I will look into it. If its simply not present, then I will try to define my own operator new macro and make it work with the crtdbg memory leak tracking.

Share this post


Link to post
Share on other sites
Max_Payne    757
Well... I almost got it to work.

I did this:


#define CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>

#define new new(s, _NORMAL_BLOCK, __FILE__, __LINE__)




I also enabled automatic memory leak checking. This results in a memory leak report that tells me in which file and line the leaked data was allocated... *But*, it only works for my main binary. I happen to have several modules (dynamically linked DLLs). They also include the above code, and the memory leak report tracks memory leaks from the modules. *However*, it does not keep track of the line numbers, it just says something along the lines of: #File Error#: {158} normal block at ...

My guess is that the data to track the memory allocations is somehow separate from the memory heap itself, and is separate for external modules. This is rather unfortunate. I would really like to be able to track memory leaks everywhere, and know where they were allocated. Unfortunately, I don't know if there is a way to make the crtdbg code do this (I suspect not)... And even if I can, technically, program my own memory leak tracker, its annoying because I have to link the allocation map dynamically by hand. Since I need access to the engine to do this, it means I must count on the idea that nothing is allocated before the DLL interface is initialized. Which is feasible, but somewhat annoying.

Could anyone tell me if there is a mechanism in DLLs that is similar to GetProcAddress(), but for exporting a variable? Or something that could automatically link an exported variable when the DLL is loaded?

Share this post


Link to post
Share on other sites
Jan Wassenberg    999
Quote:
Could anyone tell me if there is a mechanism in DLLs that is similar to GetProcAddress(), but for exporting a variable? Or something that could automatically link an exported variable when the DLL is loaded?

GetProcAddress simply scans the export dir, so it works for functions as well as variables. Exporting variables is not something you want to do, though, because it makes delay-loading impossible.
I could rant here about how DLLs for low-level engine components are probably not a good idea at all, but that's most likely not what you want to hear ;)
As for leak detection, here is the way to go: determine owner of an allocation by walking the call stack and derive symbol name+file+line from debug information. Although outdated (update is forthcoming), the code here covers the basics of what it takes to hopefully make leaks no longer an issue :)

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this