Critique my memory tracker

Started by
6 comments, last by Enigma 17 years ago
Until we get a Peer Review Forum, this can go here [smile] Anyway, I've written a memory manager which is pretty unobtrusive. It doesn't require macro hackery like MMGR, it tracks STL allocations, it works in x64, and is thread safe. And it gives the file, line number and function name of allocations. On the down side, it only works in debug builds, only on Windows, it's probably slow, and I don't like the stack walking code very much, it seems a bit hacky. It doesn't currently have any support for breaking on allocations since I haven't needed that support, but it should be easy enough to understand and modify. So here's the source code: PMemory.h, PMemory.cpp. The source is a little long to post here on a [ source ] tag, but the links are straight to the .cpp and .h files. It won't compile straight off, there's 3 changes you'll need to make.
  1. #define USE_MMGR in your peoject settings, or remove all references to USE_MMGR
  2. Remove the #include of EMemory.h
  3. Remove the Assert() calls, or implement your own Assert() macro
The stack walking code "works on my machine", you might need to change the number of frames to walk on your own machines. Anyway, I'd be glad to hear any comments / suggestions / insults etc [smile] Cheers, Steve
Advertisement
I've just built it into my project, I'll give some more feedback once I give it a bit more use.
Sure is a big 'ol world.
I would suggest that before you write your own memory management tools, you get familiar with what the development environment you're using already provides. If you're using DevStudio on Windows, then take a look at these articles about the debug heap. There's a huge amount of information available about memory usage. From personal experiance, I've found I've never needed any additional memory diagnostics to find problems.

Skizz
Quote:Original post by Skizz
I would suggest that before you write your own memory management tools, you get familiar with what the development environment you're using already provides. If you're using DevStudio on Windows, then take a look at these articles about the debug heap. There's a huge amount of information available about memory usage. From personal experiance, I've found I've never needed any additional memory diagnostics to find problems.

Skizz
I've skimmed over that before, although the last time I tried it, it didn't tell me the file:line or function of the allocation. Another thing you could do with my allocator, is store a full call stack, or at least several frames, that would allow you to more precisely find where leaks are comming from (E.g. STL containers).
Although file::line information might be useful to find the location of a leak, for example, in many instances it's not enough:
for (int i = 0; i < 100000 ; ++i){  // do something that allocates memory}

How would file::line help if iteration 45623 leaks memory but the rest don't? Using the _CrtSetDbgFlag function to set the _CRTDBG_LEAK_CHECK_DF flag produces a list of all the leaky memory in DevStudio's output window when the program exits. The important field here is the allocation number (in '{}'s IIRC). You can then use _CrtSetBreakAlloc to specify a break point for that allocation request. So, your code would be:
void main (args){   // set _CRTDBG_LEAK_CHECK_DF   if (there's an argument)   {      _CrtSetBreakAlloc (atoi (arg));   }   // rest of program}

and you would do the following:
  1) run program  2) if no leaks, end  3) get allocation number  4) run program with allocation number  5) program breaks to debugger when allocation made

Now, this is far better than just file::line information as it not only gives you the location but the context as well and the ability to debug and poke around the system.

Skizz
Breaking on an allocation ID is trivial to do, in fact the code is already there in the .cpp, there's just no function to set the break on alloc number [smile]
Breaking on an alloc ID only helps if your program allocates memory the exact same way each run. Admittedly, you'd try to do this anyway if you were tracking a memory leak, but there are many cases where it's not possible, such as the leak occuring at some allocation during the game. You'd have to run the game in the exact same way (including resetting all RNGs to the same value and ensuring the user input happens on the same frames, etc) to have the alloc ID alone be of any use.

Every other memory manager I've looked at (Although I haven't looked at many) either doesn't give you file and line information, or uses macro hackery to get you it, which breaks in some cases (E.g. MMGR using member operator new).
You're right, allocation ID does require consistent memory allocation and that is the main drawback to that method.

Just out of interest, what's the overhead of walking the stack and getting the caller location? It has to be said that the macro method for getting allocation location has very minimal overhead and is more portable (if that's important) but does suffer from having to code differently (i.e. not use 'new' directly).

Skizz
  • __PMEMORY_H__. Reserved symbol. Evil (Steve).

  • You seem to be using std namespace symbols in headers without namespace prefixes. I hope you don't have a using namespace std; in a header. That would just be Evil (Steve).

  • Hungarian Notation. Ugh.

  • Varargs. Ugh. Yes, I realise you're using it to avoid recursive calls to the memory manager, but still Ugh.

  • Your operator new([])s don't follow the standard on allocation failure. You should loop, trying to allocate each time and, if failing to allocate either call the currently installed new_handler (if there is one) and loop again or throw std::bad_alloc if there is no currently installed new_handler.

  • I'm pretty sure local static variables are not thread-safe.

  • Your returned memory is not guaranteed to satisfy alignment requirements.

  • It's possible that IMAGEHLP_LINE64::FileName is a pointer into the executables debug symbols, in which case you can just store the pointer rather than keeping an oversized buffer and copying the string yourself.
All in all, it looks pretty good. I've tried writing a similar implementation myself, but walking the stack manually (and hooking in to gcc's debug symbols). It works fairly well, although these days I manually allocate memory so infrequently I find a full-blown memory manager to be a bit over-the-top.

Σnigma

This topic is closed to new replies.

Advertisement