Memory Analyzer v1.0 demo

Four months ago we’ve written about a new tool we are currently developing, for tracking memory allocations in real-time. Since then, the code has changed a lot. It has been cleaned up and improved, both memory and performance wise, and today we are proud to release a demo version of it. You can grab it right now or you can continue reading, in order to become familiar with its features.

How it works
Memory Analyzer keeps track of each and every allocation made by the target application, including allocations made by all of its loaded modules. In order to accomplish that, it injects a DLL into the target’s address space and attaches hooks to every memory related function. Whenever a (re)allocation/deallocation occurs, the injected DLL (client) sends a message to Memory Analyzer (server). The server is responsible for keeping all the information needed in order to find the newly allocated memory block later on, or, in the case of a deallocation, it removes the memory block from the list of active blocks. At any time, the user is able to inspect all active memory blocks, with information about the location in the code where the allocation happened, the heap used for the allocation, as well as the size of the block.

The injection method used in the current version of Memory Analyzer, is able to capture allocations/deallocations made even before the main function of the application starts (e.g. allocations made inside constructors of static/global objects). This is the biggest difference from the previous version of Memory Analyzer, where there was no injection mechanism and the user was responsible for re-linking his application with the tracking DLL. Injection, in this case, happened at the time a static instance of the tracking object was initialized. Since the user isn’t able to control the order of initialization of static/global variables, the old method didn’t guarantee that allocations made by code executed before main() will be caught. The new method doesn’t require recompilation of the target application and it can be used right away. The downside is that there are a lot more captured allocations, and as a result there might be noise in the output.

Currently, the injected DLL includes hooks for the following functions:

  • HeapXXX, LocalXXX and GlobalXXX from kernel32.dll, where XXX is Alloc, ReAlloc and Free
  • HeapDestroy from kernel32.dll
  • scalar and vector versions of operators new and delete from mfc80.dll, mfc80d.dll, mfc90d.dll and mfc90.dll
  • scalar and vector versions of operators new and delete from msvcr90d.dll, msvcr90.dll, msvcr80d.dll and msvcr80.dll
  • malloc/calloc/realloc/free and all of their _aligned and _aligned_offset versions from msvcr90d.dll, msvcr90.dll, msvcr80d.dll and msvcr80.dll

As you can see the list isn’t complete yet, but we hope that we have covered the majority of cases. Things still missing are VirtualXXX(Ex) functions from kernel32.dll and unicode versions of MFC DLLs.

The demo has been tested successfully on the following systems:

  • Windows XP SP2 x86 with VS2005 and VS2008
  • Windows Vista Home Premium x86 and Ultimate SP1 x86 with VS2008
  • Windows 7 RC1 (build 7100) x64 with VS2008

Configuration
Configuration is really simple. The following screenshot shows Memory Analyzer’s configuration tab.

Configuration tab
  • Target: The path to the target application. Use the Browse button to find the location of the executable on hdd.
  • Command Line: Optional command line arguments for the target application.
  • PDB Search Path: Optional path for .pdb files. PDB files are used to get function name and line number information for the callstack. If you need accurate callstack info, it is advised to specify the correct path. Note that you can use the Browse button multiple times in order to build a more complex path.
  • Module List (and enclosed controls): Used to specify which modules we are actually interested in monitoring, instead of monitoring every one of them. Currently, you can’t exclude any modules from tracking. This is something we are working on.
  • Max Callstack Frames: The maximum number of callstack frames reported for each allocation. Currently it is hard-coded to 5 in order to keep the CPU usage and memory requirements as low as possible.
  • Hide internal allocations: Enables/disables the reporting of known allocations, such as those made internally by MFC or the CRT. If you would like to see all the allocations made by your application, uncheck this option. Currently, the list of known internal allocations is really small, so you will still get some of them in the output.
  • Hook kernel32.dll: Check it in order to intercept all kernel32.dll functions (see the list above). This option has been introduced in order to overcome a bug with dbghelp.dll. If you encounter an ACCESS_VIOLATION message in the debugger’s log (last tab), while having this option checked, uncheck it and try again. Unfortunately this is a random bug (the hardest kind) and usually it seems to be fixed with a system restart. If you encounter this bug, and it’s persistent, please report back.

When you are done with the configuration, hit Go and switch to another tab in order to observe the output.

Output

Statistics tab

The first tab (“General”) reports statistics about the allocations made by the target application. The names should be self-explanatory. The graph displays the total memory used by the application during its lifetime, and it refreshes every 200 msec (approximately). Note that both LocalXXX and GlobalXXX set of functions are counted as HeapXXX calls.

Active allocations tab

The second tab (“Allocations”) displays all active memory blocks. In order to refresh the list, hit the Refresh button. Please note that currently the list box is refilled every time you press the Refresh button, so it might take a while if there are many (e.g. thousands) active allocations. When the application finishes, this tab will display all memory blocks which haven’t been freed.

Due to the way Memory Analyzer injects the DLL into the target process, you might get memory leaks from places you can’t control. Those can be either true memory leaks, or blocks of memory which haven’t been freed early enough to be caught by the injected DLL. Examples of such leaks are memory blocks allocated by the CRT during initialization, as well as memory blocks allocated by standard library functions (like printf, etc.). Currently, Memory Analyzer has a simple suppression mechanism implemented which is used to hide some of those allocations. Unfortunately, it’s not exposed to the user right now. We are working on it, and it will probably be available in the next version. For now, we’ve suppressed some of those allocations for you.

Fragmentation tab

The next tab is the fragmentation tab. At the top of the page there is a Refresh button. By pressing it the list of heaps will be populated with all the heaps currently used by the application. By changing the heap handle, the view changes to reflect the layout of the new heap. Something to remember when looking at this view is that Memory Analyzer intercepts (and reports) the first function which is responsible for an allocation (i.e. malloc instead of RtlAllocateHeap). This means that the reported memory block doesn’t contain any necessary header structure used (e.g.) by the CRT memory manager. As a result, memory blocks which should be next to each other, might appear with a small gap between them. Also note that realloc(NULL, 0) returns a valid pointer (a memory block with zero size) and this might appear as a discontinuity between free blocks. By clicking on any one of the red/orange blocks, you will get its callstack and its size.

Histogram tab

Next is the histogram tab. By pressing the Refresh button you will get a histogram of all the allocations made by the application, grouped in buckets of the next power of two.

Memory log tab

After the histogram there is the memory log tab. By pressing the Start/Stop button you can enable logging of all the operations tracked by Memory Analyzer. Pressing Clear will clear the list. This view is useful if you would like to see which places in your code allocate memory at run-time (e.g. inside your renderer’s inner loop). As with all the previous tabs (except the histogram tab) you get a callstack and other information about the operation being logged.

Debugger's log

The final tab is the debugger’s output tab. Here you will find information about the state of the injection process (watch out for ACCESS_VIOLATION exceptions during injection) as well as other debugger related stuff. Since the target application runs inside a debugger, you can’t attach another debugger to it. For making you life a bit easier, Memory Analyzer captures and reports all OutputDebugString() messages. This way you can output meaningful messages from your application in order to make debugging easier. Note that this is needed in case a problem appears only when running the target inside Memory Analyzer’s debugger, and you would like to check what’s causing it.

Prerequisites
The only thing required in order to use Memory Analyzer (except from the actual application) is Microsoft Visual C++ 2008 Feature Pack Redistributable Package (x86). If you have it already installed, you won’t need anything else. If you are not sure, try running the application and if you get an error, download it and install it. Since this is a demo version we didn’t build an installer to automate the process. Future versions might get an installer.

Future work
Except from the things mentioned in the previous post (such as the implementation of an API for sending custom messages from the target app to the server, a way to compare two dumps, etc.), we are currently implementing a system for suppressing allocations the user doesn’t care about. Due to the nature of the application and the environment it is supposed to run, it makes a perfect candidate for a Visual Studio plug-in. We haven’t looked into that direction yet, but it’s something we are considering. If you have something in mind which might fit into Memory Analyzer, please say it. It might appear in a future version!

Download
Get the demo here.

Thanks for reading. If you try the demo, please report back any problems/bugs you encounter. We will also be very happy to hear that you used it successfully.

Happy memory tracking!

Tags: ,

8 Responses to “Memory Analyzer v1.0 demo”

  1. Kevin Says:

    It would be really helpful if the Fragmentation view was resizable. Even at 32 bytes, the scrolling could go on for quite a while.

    Dragging the scrollbar doesn’t seem to work. If I drag the view often stays at the top of the selected heap.

    If you have a really small heap that doesn’t fill the view, the view doesn’t completely repaint, so you see the old data from the previously viewed heap at the end. Clicking on the scrollbar appears to force a refresh.

    Note that I saw these just by using your tool with Excel (2003).

    Is there some way to get a single view of the entire virtual memory (not specific to any given heap)? This would be helpful in seeing the overall picture of the application’s memory when I don’t care which heap the allocation came from.

  2. JD Says:

    Hello Kevin and welcome,

    First of all, thanks for the feedback. Some of the things you mentioned (like resizing all controls when the window was resized) were in our TODO list. We have fixed the scrolling issue and we have added mouse wheel support and larger block sizes (up to 256 bytes).

    You can download the new version of Memory Analyzer from here: Memory Analyzer v1.0 (build 20090607). I’ve also updated the original post in order to point to the new archive.

    The new version also includes some small additions (e.g. you can now manually specify which functions you would like to suppress through an external file) and bug fixes.

    Regarding the single view of the entire virtual memory. Unfortunately there isn’t a way right now to do that. We plan to add one more view for that, with color encoded fixed size blocks, and without callstack and source code info. If you have something specific in mind about how this view should look, please share it with us.

    Thanks again for testing Memory Analyzer and for the feedback.

  3. JD Says:

    We have missed a small bug in the code which prevented Memory Analyzer from reporting allocations when “Hide internal allocations” was checked. It’s now fixed and you can download the new version from here: Memory Analyzer v1.0 (build 20090611). The original post was updated to point to the new version.

  4. Anders Says:

    Is it possible to set the working directory for the selected application? I need to start my application from within a specific folder. Thanks.

  5. JD Says:

    Hi and sorry for the delayed reply. Yes it’s possible but it’s not implemented yet. We’ll try adding the necessary controls and we will release a new version in the following days.

  6. JD Says:

    You can download the new version from here: Memory Analyzer v1.0 (build 20090929). In the Setup view there is a new edit box along with a browse button for setting the working directory of the new process. If it’s left blank the working directory is the same as before (Memory Analyzer’s directory). Hope it works as you expected.

  7. Thom Says:

    This looks like a great tool that would really help me out. Unfortunately, it throws an exception immediately upon startup. (Unhandled exception at 0x003ae2e0 in MemoryAnalyzer.exe: 0xC0000005: Access violation reading location 0×00000000.)

    This is on Win7 Enterprise (32-bit), and an application that was built using VS2008.

    Has the MemAnalyzer had any updates since 9/2009 that might help me get it running?

    Thanks!
    Thom

  8. JD Says:

    Hello Thom and thanks for downloading Memory Analyzer. We are really sorry that it doesn’t work for you. We’ll try to upload a new version later today which hopefully will fix the problem you are facing.

    In the meantime, can you also post the modules base address or the offset to the exception code in order to debug it and see what’s going on. The address at which the exception occurs is, unfortunately, useful only if it’s relative to the module’s base.

    Thanks again for trying Memory Analyzer.

Leave a Reply


five − = 3