This section of the archives stores flipcode's complete Developer Toolbox collection, featuring a variety of mini-articles and source code contributions from our readers.

| Finding Memory Usage Patterns In VC++ Submitted by

As a follow-up to Thomas Rued's TOTD about finding memory leaks, I'd like to contribute an extension of my own.
The original tip made use of the debug-heap facilities of Windows to get lists of memory leaks at the end of the program's execution. But, what happens when, instead of finding leaks, your problem is finding why a particular portion of your program is using more memory than it should? In general, how can you find out how your program uses the heap?
My solution is to use the debug heap facilities, and extract as much information as I can from them, formatting it in a useful manner.
So, this is the contents of the header file that we will use:

    // MemDebugStats.h : Interface of the heap memory-dump library
    // Author: Juan Carlos "JCAB" Arévalo Baeza <mailto:jcab@roningames.com
    // (C) 2000-2001 Ronin Entertainment <http://www.roningames.com
    // You may use this as you like, as long as you don't remove the copyright.

    #ifndef MemDebugStats_H_INCLUDED
    #define MemDebugStats_H_INCLUDED
    #pragma once

    #include <CrtDbg.h

    struct MemDebugSnapshot {
         _CrtMemState memState;
         int *hookedBlock;
         MemDebugSnapshot(): hookedBlock(0) {}
         ~MemDebugSnapshot() { delete hookedBlock; }
    };

    void GetMemSnapshot(MemDebugSnapshot &snap);
    void DumpMemStats(MemDebugSnapshot &snap, const char *file, unsigned int line);
    void DumpMemStats(const char *file, unsigned int line);
    void DumpShortMemStats(const char *file, unsigned int line);

    #define DUMP_MEM_STATS(s)           DumpMemStats((s), __FILE__, __LINE__)
    #define DUMP_CURRENT_MEM_STATS()    DumpMemStats(__FILE__, __LINE__)
    #define DUMP_SHORT_MEM_STATS()      DumpShortMemStats(__FILE__, __LINE__)

    #define DBG_NEW new(_NORMAL_BLOCK, __FILE__, __LINE__)

    #endif

This can be used as follows:
(note that we do all our allocations through the debug heap functions. This is done using the DBG_NEW macro, which is the same as the redefinition of new that is done in MFC, except that I don't like redefining new due to the fact that it makes impossible the use of the normal placement-new)

    #include "MemDebugStats.h"

    #include <stdLib.h

    int APIENTRY WinMain(HINSTANCE hInstance,
                          HINSTANCE hPrevInstance,
                          LPSTR     lpCmdLine,
                          int       nCmdShow)
    {
         {
             // Second, we grab a MemDebugSnapshot instance.
             // This will hold the current state of the heap,
             // at the time GetMemSnapshot() is called.
             // It's surprisingly very cheap in CPU and memory.
             MemDebugSnapshot snapshot;
             GetMemSnapshot(snapshot);

             // Now, we allocate a bunch of crap.
             int i;
             for (i = 0; i < 1000; ++i) {
                 DBG_NEW char[rand() & 0xFFF];   // Large
                 DBG_NEW char[rand() & 0x0FF];   // Small
             }

             // And this dumps out the heap memory statistics
             // of all allocations that happened since the
             // snapshot was taken.
             DUMP_MEM_STATS(snapshot);
         }

         {
             // Again, we grab a new MemDebugSnapshot instance.
             MemDebugSnapshot snapshot;
             GetMemSnapshot(snapshot);

             // Now, we allocate just a few bytes.
             DBG_NEW int[7]; // 28 bytes

             // And again, this dumps out the heap memory
             // statistics of all allocations that happened
             // since the new snapshot was taken.
             DUMP_MEM_STATS(snapshot);
         }

         // Finally, this dumps all the memory ever allocated.
         // So, it includes the allocations made above,
         // and anything else that might happen behind our backs.
         DUMP_CURRENT_MEM_STATS();

         return 0;
    }

So, how does this do its magic?
First, there's a debug-heap function to trigger a memory-leak dump: _CrtMemDumpAllObjectsSince(). This just does the same thing that MFC does automatically at the end of program execution, only you can call it anytime.
Second, you can set up a callback function to be called by _CrtMemDumpAllObjectsSince(), so that instead of dumping the result to the debug output window, it sends it to your callback instead. You can then parse this output to extract all the information.
Third, there is a debug-heap function called _CrtMemCheckpoint() that implements the main functionality of GetMemSnapshot(). GetMemSnapshot() has to do a little more work, because of the way _CrtMemCheckpoint() works. Basically, when a new block gets allocated in the debug heap, its pointer is added to the tail of a linked list. _CrtMemCheckpoint() just stores a pointer to the current tail of this list, so that, when _CrtMemDumpAllObjectsSince() is called, it can do the dump by going through the list, starting at that tail. This dumps only those blocks that were allocated since the checkpoint. The problem happens when the object whose pointer is used as the checkpoint gets freed before _CrtMemDumpAllObjectsSince() is called. In that case, the dump will be garbage, and it could possibly crash. So GetMemSnapshot() allocates a small block (an int) just before calling _CrtMemCheckpoint(), and stores its pointer together with the checkpoint tail pointer, so that it can be freed only when we don't need the checkpoint any more.
And that's it. The rest is all just data pushing and organizing in several useful ways.
There is some more code in there that hasn't proven terribly useful, but someone might get a kick out of it. Especially the use of the ToolHelp functions to generate a module list and to walk the Windows heaps. Note that walking the Windows heaps could get interesting, as it'll allow you to see memory that's usually hidden. For example, D3D sysmem buffers and such. You'd have to eyeball it, though.
I'm including the actual code as an attachment because it's a trifle too big to include inline here.

Currently browsing [MemDebug.zip] (8,166 bytes) - [MemDebug.cpp] - (1,688 bytes)

    // MemDebug.cpp : Defines the entry point for the application.
    //

    #include "stdafx.h"

    #include "MemDebugStats.h"

    #include <stdLib.h>

    int APIENTRY WinMain(HINSTANCE hInstance,
                         HINSTANCE hPrevInstance,
                         LPSTR     lpCmdLine,
                         int       nCmdShow)
    {
        {
            // Second, we grab a MemDebugSnapshot instance.
            // This will hold the current state of the heap,
            // at the time GetMemSnapshot() is called.
            // It's surprisingly very cheap in CPU and memory.
            MemDebugSnapshot snapshot;
            GetMemSnapshot(snapshot);

            // Now, we allocate a bunch of crap.
            int i;
            for (i = 0; i < 1000; ++i) {
                DBG_NEW char[rand() & 0xFFF];   // Large
                DBG_NEW char[rand() & 0x0FF];   // Small
            }

            // And this dumps out the heap memory statistics
            // of all allocations that happened since the
            // snapshot was taken.
            DUMP_MEM_STATS(snapshot);
        }

        {
            // Again, we grab a new MemDebugSnapshot instance.
            MemDebugSnapshot snapshot;
            GetMemSnapshot(snapshot);

            // Now, we allocate just a few bytes.
            DBG_NEW int[7]; // 28 bytes

            // And again, this dumps out the heap memory
            // statistics of all allocations that happened
            // since the new snapshot was taken.
            DUMP_MEM_STATS(snapshot);
        }

        // Finally, this dumps all the memory ever allocated.
        // So, it includes the allocations made above,
        // and anything else that might happen behind our backs.
        DUMP_CURRENT_MEM_STATS();

    	return 0;
    }

Currently browsing [MemDebug.zip] (8,166 bytes) - [StdAfx.h] - (773 bytes)

    // stdafx.h : include file for standard system include files,
    //  or project specific include files that are used frequently, but
    //      are changed infrequently
    //

    #if !defined(AFX_STDAFX_H__A9DB83DB_A9FD_11D0_BFD1_444553540000__INCLUDED_)
    #define AFX_STDAFX_H__A9DB83DB_A9FD_11D0_BFD1_444553540000__INCLUDED_

    #if _MSC_VER > 1000
    #pragma once
    #endif // _MSC_VER > 1000

    #define WIN32_LEAN_AND_MEAN		// Exclude rarely-used stuff from Windows headers

    #include <windows.h>

    // TODO: reference additional headers your program requires here

    //{{AFX_INSERT_LOCATION}}
    // Microsoft Visual C++ will insert additional declarations immediately before the previous line.

    #endif // !defined(AFX_STDAFX_H__A9DB83DB_A9FD_11D0_BFD1_444553540000__INCLUDED_)

Currently browsing [MemDebug.zip] (8,166 bytes) - [StdAfx.cpp] - (295 bytes)

    // stdafx.cpp : source file that includes just the standard includes
    //	MemDebug.pch will be the pre-compiled header
    //	stdafx.obj will contain the pre-compiled type information

    #include "stdafx.h"

    // TODO: reference any additional headers you need in STDAFX.H
    // and not in this file

Currently browsing [MemDebug.zip] (8,166 bytes) - [MemDebugStats.h] - (1,075 bytes)

    // MemDebugStats.h : Interface of the heap memory-dump library
    // Author: Juan Carlos "JCAB" Arévalo Baeza <mailto:jcab@roningames.com>
    // (C) 2000-2001 Ronin Entertainment <http://www.roningames.com>
    // You may use this as you like, as long as you don't remove the copyright.

    #ifndef MemDebugStats_H_INCLUDED
    #define MemDebugStats_H_INCLUDED
    #pragma once

    #include <CrtDbg.h>

    struct MemDebugSnapshot {
    	_CrtMemState memState;
    	int *hookedBlock;
    	MemDebugSnapshot(): hookedBlock(0) {}
    	~MemDebugSnapshot() { delete hookedBlock; }
    };

    void GetMemSnapshot(MemDebugSnapshot &snap);
    void DumpMemStats(MemDebugSnapshot &snap, const char *file, unsigned int line);
    void DumpMemStats(const char *file, unsigned int line);
    void DumpShortMemStats(const char *file, unsigned int line);

    #define DUMP_MEM_STATS(s)			DumpMemStats((s), __FILE__, __LINE__)
    #define DUMP_CURRENT_MEM_STATS()	DumpMemStats(__FILE__, __LINE__)
    #define DUMP_SHORT_MEM_STATS()		DumpShortMemStats(__FILE__, __LINE__)

    #define DBG_NEW new(_NORMAL_BLOCK, __FILE__, __LINE__)

    #endif

Currently browsing [MemDebug.zip] (8,166 bytes) - [MemDebugStats.cpp] - (19,517 bytes)

    // MemDebugStats.cpp : Implementation of the heap memory-dump library
    // Author: Juan Carlos "JCAB" Arévalo Baeza <mailto:jcab@roningames.com>
    // (C) 2000-2001 Ronin Entertainment <http://www.roningames.com>
    // You may use this as you like, as long as you don't remove the copyright.

    #include "stdafx.h"

    #include "MemDebugStats.h"

    #pragma warning(disable: 4786)	// identifier was truncated to '255' characters in the debug information

    #include <windows.h>
    #include <CrtDbg.h>
    #include <TLHelp32.h>

    #include <stdlib.h>
    #include <stdio.h>
    #include <stdarg.h>
    #include <string.h>

    #include <map>
    #include <string>

    // Unrelated common definitions

    typedef unsigned char uchar;
    typedef unsigned int  uint;

    #define RONIN_PRINTF(s,n) { \
    	char ronin__s[n];					\
    	va_list ronin__list;				\
    										\
    	va_start(ronin__list, s);			\
    	vsprintf(ronin__s, s, ronin__list);	\
    	va_end(ronin__list);				\
    	s = ronin__s;						\
    }

    // Prints out to the file AND to OutputDebugString().
    static void MultiPrintf(FILE *f, const char *fmt, ...)
    {
    	RONIN_PRINTF(fmt, 4096);
    	if (f) {
    		fprintf(f, "%s", fmt);
    	}
    	OutputDebugString(fmt);
    }

    static void DumpProcessInfo(FILE *f)
    {
    	SYSTEM_INFO si;
    	GetSystemInfo(&si);

    	MultiPrintf(f, "\n-------------------- Current process info\n\n");

    	HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPHEAPLIST | TH32CS_SNAPMODULE, 0);

    	DWORD dataAlloc	= 0;

    	MODULEENTRY32 me;
    	me.dwSize = sizeof(me);
    	bool moduleValid = !!Module32First(snapshot, &me);
    	while (moduleValid) {
    		if ((DWORD)me.modBaseAddr >= (DWORD)si.lpMinimumApplicationAddress && (DWORD)me.modBaseAddr < (DWORD)si.lpMaximumApplicationAddress) {
    			MultiPrintf(f, "Process module: %8x, %13.3f Kbytes, %-32s : %s\n", me.modBaseAddr, me.modBaseSize / 1024.F, me.szModule, me.szExePath);

    			dataAlloc += me.modBaseSize;
    		}

    		moduleValid = !!Module32Next(snapshot, &me);
    	}

    	MultiPrintf(f, "\nTotal process data: %13.3f Kbytes\n\n", dataAlloc / 1024.F);

    	me.dwSize = sizeof(me);
    	moduleValid = !!Module32First(snapshot, &me);
    	while (moduleValid) {
    		if (!((DWORD)me.modBaseAddr >= (DWORD)si.lpMinimumApplicationAddress && (DWORD)me.modBaseAddr < (DWORD)si.lpMaximumApplicationAddress)) {
    			MultiPrintf(f, "System module: %8x, %13.3f Kbytes, %-32s : %s\n", me.modBaseAddr, me.modBaseSize / 1024.F, me.szModule, me.szExePath);
    		}

    		moduleValid = !!Module32Next(snapshot, &me);
    	}

    	// The following is VERY slow on Win2000, so I usually keep it disabled.
    	// Win98 is reasonably fast, thoguh. Dunno why.
    /*
    	DWORD totalAlloc	= dataAlloc;
    	DWORD totalFree		= 0;

    	HEAPLIST32 hl;
    	hl.dwSize = sizeof(hl);
    	bool heapValid = !!Heap32ListFirst(snapshot, &hl);
    	while (heapValid) {
    		if (hl.dwFlags & HF32_DEFAULT) {
    			MultiPrintf(f, "Default heap found!!\n");
    		} else {
    			MultiPrintf(f, "Heap found!!\n");
    		}

    		DWORD heapAlloc	= 0;
    		DWORD heapFree	= 0;

    		DWORD numHeapAlloc	= 0;
    		DWORD numHeapFree	= 0;

    		HEAPENTRY32 he;
    		he.dwSize = sizeof(he);
    		bool entryValid = !!Heap32First(&he, hl.th32ProcessID, hl.th32HeapID);
    		while (entryValid) {
    			if (he.dwFlags == LF32_FIXED) {
    				// Uncomment to log out every single "fixed" block.
    //				MultiPrintf(f, "    Block: %8x, %13.3f Kbytes, fixed\n", he.dwAddress, he.dwBlockSize / 1024.F);
    				heapAlloc += he.dwBlockSize;
    				++numHeapAlloc;
    			} else if (he.dwFlags == LF32_FREE) {
    				// Uncomment to log out every single "freed" block.
    //				MultiPrintf(f, "    Block: %8x, %13.3f Kbytes, free\n", he.dwAddress, he.dwBlockSize / 1024.F);
    				heapFree += he.dwBlockSize;
    				++numHeapFree;
    			} else if (he.dwFlags == LF32_MOVEABLE) {
    				// Uncomment to log out every single "moveable" block.
    //				MultiPrintf(f, "    Block: %8x, %13.3f Kbytes, moveable\n", he.dwAddress, he.dwBlockSize / 1024.F);
    				heapAlloc += he.dwBlockSize;
    				++numHeapAlloc;
    			} else {
    				MultiPrintf(f, "    Block: %8x, %13.3f Kbytes, <unknown: %8x>\n", he.dwAddress, he.dwBlockSize / 1024.F, he.dwFlags);
    			}

    			entryValid = !!Heap32Next(&he);
    		}

    		MultiPrintf(f, "    Heap: %13.3f Kbytes allocated, %13.3f Kbytes free\n", heapAlloc / 1024.F, heapFree / 1024.F);

    		totalAlloc	+= heapAlloc;
    		totalFree	+= heapFree;

    		heapValid = !!Heap32ListNext(snapshot, &hl);
    	}

    	MultiPrintf(f, "Total: %13.3f Kbytes allocated, %13.3f Kbytes free\n", totalAlloc / 1024.F, totalFree / 1024.F);
    //*/

    	CloseHandle(snapshot);
    }

    static void DumpGlobalInfo(FILE *f)
    {
    	MEMORYSTATUS ms;
    	GlobalMemoryStatus(&ms);

    	MultiPrintf(f, "\n-------------------- System info\n\n");

    	MultiPrintf(f, "Memory load:         %8d %%\n", ms.dwMemoryLoad);
    	MultiPrintf(f, "Memory total:        %12.3f MB\n",  ms.dwTotalPhys								/ (1024.F * 1024.F));
    	MultiPrintf(f, "Memory used:         %12.3f MB\n", (ms.dwTotalPhys - ms.dwAvailPhys)			/ (1024.F * 1024.F));
    	MultiPrintf(f, "Memory available:    %12.3f MB\n",  ms.dwAvailPhys								/ (1024.F * 1024.F));
    	MultiPrintf(f, "Swap file total:     %12.3f MB\n",  ms.dwTotalPageFile							/ (1024.F * 1024.F));
    	MultiPrintf(f, "Swap file in use:    %12.3f MB\n", (ms.dwTotalPageFile - ms.dwAvailPageFile)	/ (1024.F * 1024.F));
    	MultiPrintf(f, "Swap file available: %12.3f MB\n",  ms.dwAvailPageFile							/ (1024.F * 1024.F));
    	MultiPrintf(f, "Virtual total:       %12.3f MB\n",  ms.dwTotalVirtual							/ (1024.F * 1024.F));
    	MultiPrintf(f, "Virtual in use:      %12.3f MB\n", (ms.dwTotalVirtual - ms.dwAvailVirtual)		/ (1024.F * 1024.F));
    	MultiPrintf(f, "Virtual available:   %12.3f MB\n",  ms.dwAvailVirtual							/ (1024.F * 1024.F));
    	MultiPrintf(f, "\n");
    }

    void GetMemSnapshot(MemDebugSnapshot &snap)
    {
    	delete snap.hookedBlock;
    	snap.hookedBlock = new int(0xBAADF0F0);
    	_CrtMemCheckpoint(&snap.memState);
    }

    #ifndef NDEBUG

    struct FileLine {
    	std::string	name;
    	int			line;

    	FileLine(): name(), line(0) {}
    	FileLine(const std::string &name_, int line_): name(name_), line(line_) {}

    	friend bool operator <(const FileLine &fl1, const FileLine &fl2) {
    		const int comp = strcmp(fl1.name.c_str(), fl2.name.c_str());
    		if (comp < 0) return true;
    		if (comp > 0) return false;
    		return fl1.line < fl2.line;
    	}
    };

    struct SizeInfo {
    	int numBlocksPerSize[32];
    	int totalSizePerSize[32];
    	int totalBlocks;
    	int totalSize;

    	SizeInfo(): totalBlocks(0), totalSize(0) {
    		memset(&numBlocksPerSize, 0, sizeof(numBlocksPerSize));
    		memset(&totalSizePerSize, 0, sizeof(totalSizePerSize));
    	}

    	SizeInfo &operator+=(const SizeInfo &other) {
    		uint i;
    		for (i = 0; i < 32; ++i) {
    			numBlocksPerSize[i] += other.numBlocksPerSize[i];
    			totalSizePerSize[i] += other.totalSizePerSize[i];
    		}
    		totalBlocks	+= other.totalBlocks;
    		totalSize	+= other.totalSize;
    		return *this;
    	}

    	SizeInfo &operator-=(const SizeInfo &other) {
    		uint i;
    		for (i = 0; i < 32; ++i) {
    			numBlocksPerSize[i] -= other.numBlocksPerSize[i];
    			totalSizePerSize[i] -= other.totalSizePerSize[i];
    		}
    		totalBlocks	-= other.totalBlocks;
    		totalSize	-= other.totalSize;
    		return *this;
    	}
    };

    struct FileLineSizeInfo: FileLine, SizeInfo {
    	FileLineSizeInfo() {}
    	FileLineSizeInfo(const FileLine &fl, const SizeInfo &si):
    		FileLine(fl),
    		SizeInfo(si)
    	{}
    };

    struct FileSizeInfo: SizeInfo {
    	std::string	name;

    	FileSizeInfo() {}
    	FileSizeInfo(const std::string &n, const SizeInfo &si):
    		name(n),
    		SizeInfo(si)
    	{}
    };

    // There's no way to do without the file-scope variables,
    // because the report hook function doesn't have the extra
    // "custom data" pointer that EVERY C-style callback must have.
    // Bad move by the boys at Microsoft.

    static std::string	g_LastFName	= std::string();
    static int			g_LastLine	= 0;

    static SizeInfo g_UnnamedSizeInfo;
    static SizeInfo g_TotalSizeInfo;

    static std::map<FileLine, SizeInfo>	g_FileLineMap;

    static FILE *g_Fileo = NULL;

    static void ClearFileData()
    {
    	g_LastFName	= std::string();
    	g_LastLine	= 0;

    	g_UnnamedSizeInfo;
    	g_TotalSizeInfo;

    	g_FileLineMap.clear();

    	g_Fileo = NULL;
    }

    static int GatherStatsReportHook(int reportType, char *message, int *returnValue)
    {
    	if (returnValue) {
    		*returnValue = 0;
    	}
    	int n = strlen(message) - 4;
    	if (n > 0 && message[n] == ')' && message[n+2] == ':') {
    		int e = n-1;
    		while (e > 0 && message[e] != '(') {
    			--e;
    		}
    		if (message[e] == '(') {
    			g_LastFName.assign(message, e);
    			sscanf(message+e+1, "%d", &g_LastLine);
    			return true;
    		}
    	}
    	void *ptr;
    	int size;
    	int subType = 0;
    	if (sscanf(message, "normal block at 0x%x, %d", &ptr, &size) == 2 ||
    		sscanf(message, "crt block at 0x%x, subtype %d, %d", &ptr, &subType, &size) == 3) {
    		SizeInfo *si;
    		if (!g_LastFName.empty()) {
    			si = &g_FileLineMap[FileLine(g_LastFName, g_LastLine)];
    		} else {
    			si = &g_UnnamedSizeInfo;
    		}
    		int block = 0;
    		int bsize = size;
    		while (bsize > 0 && block < 31) {
    			bsize >>= 1;
    			++block;
    		}
    		++si->numBlocksPerSize[block];
    		si->totalSizePerSize[block] += size;
    		++si->totalBlocks;
    		si->totalSize += size;
    		++g_TotalSizeInfo.numBlocksPerSize[block];
    		g_TotalSizeInfo.totalSizePerSize[block] += size;
    		++g_TotalSizeInfo.totalBlocks;
    		g_TotalSizeInfo.totalSize += size;
    		g_LastFName[0] = 0;
    		return true;
    	}
    	return true;
    }

    static int OutputReportHook(int reportType, char *message, int *returnValue)
    {
    	if (returnValue) {
    		*returnValue = 0;
    	}
    	MultiPrintf(g_Fileo, "%s", message);
    	return true;
    }

    static void ReportBlock(FILE *f, const SizeInfo &si)
    {
    	if (si.numBlocksPerSize[0] > 0) {
    		MultiPrintf(f, "    Blocks of size 0           : %6d blocks\n", si.numBlocksPerSize[0]);
    	}

    	if (si.numBlocksPerSize[1] > 0) {
    		MultiPrintf(f, "    Blocks of size 1           : %6d blocks\n", si.numBlocksPerSize[1]);
    	}

    	int bsize = 2;
    	int i;
    	for (i = 2; i < 32; ++i) {
    		if (si.numBlocksPerSize[i] > 0) {
    			MultiPrintf(f, "    Between %7d and %7d: %6d blocks, %10d bytes total, %11.2f avg. bytes/block\n", bsize, (bsize << 1) - 1, si.numBlocksPerSize[i], si.totalSizePerSize[i], (float) si.totalSizePerSize[i] / si.numBlocksPerSize[i]);
    		}
    		bsize <<= 1;
    	}
    }

    static void DumpSingleBlock(FILE *f, const char *header, const SizeInfo &si, bool fmt)
    {
    	if (fmt) {
    		if (si.totalBlocks > 0) {
    			MultiPrintf(f, "%-75s : %6d | %8d | %11.2f\n", header, si.totalBlocks, si.totalSize, (float)si.totalSize / si.totalBlocks);
    		} else {
    			MultiPrintf(f, "%-75s : %6d | %8d | %11.2f\n", header, 0, 0, 0);
    		}
    	} else {
    		if (si.totalBlocks > 0) {
    			MultiPrintf(f, "%s : Total %d blocks, %d bytes, %.2f avg bytes/block\n", header, si.totalBlocks, si.totalSize, (float)si.totalSize / si.totalBlocks);
    		} else {
    			MultiPrintf(f, "%s : Total 0 blocks, 0 bytes, 0 avg bytes/block\n", header);
    		}
    	}
    	if (!fmt) {
    		int n = 0;
    		int j;
    		for (j = 0; j < 32; ++j) {
    			if (si.numBlocksPerSize[j] > 0) {
    				++n;
    			}
    		}
    		if (n > 1) {
    			ReportBlock(f, si);
    		}
    	}
    }
    #endif

    static FILE *OpenLogFile()
    {
    	int j;
    	for (j = 0; j < 0x10000; ++j) {
    		char fname[4096];
    		sprintf(fname, "c:\\MEM%04x.log", j);
    		FILE *f = fopen(fname, "r");
    		if (f == NULL) {
    			return fopen(fname, "wt");
    		} else {
    			fclose(f);
    		}
    	}
    	return NULL;
    }

    void DumpMemStats(MemDebugSnapshot &snap, const char *file, unsigned int line)
    {
    	_CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_DEBUG);

    	FILE *f = OpenLogFile();

    	MultiPrintf(f, "\n-------------------------------------------------------------------------------\n");

    	if (file) {
    		MultiPrintf(f, "\n%s(%d): Code line that executed the dump\n\n", file, line);
    	} else {
    		MultiPrintf(f, "\nPlease, use the macros to execute the dump!!\n\n");
    	}

    #ifndef NDEBUG
    	{
    		MultiPrintf(f, "-------------------- Begin DumpMemStats\n\n");

    		_CRT_REPORT_HOOK g_OldReportHook = _CrtSetReportHook(GatherStatsReportHook);
    		_CrtMemDumpAllObjectsSince(&snap.memState);
    		_CrtSetReportHook(g_OldReportHook);

    		{
    			DumpSingleBlock(f, "Total blocks", g_TotalSizeInfo, false);
    			DumpSingleBlock(f, "Unnamed blocks", g_UnnamedSizeInfo, false);
    			SizeInfo namedSizeInfo = g_TotalSizeInfo;
    			namedSizeInfo += g_UnnamedSizeInfo;
    			DumpSingleBlock(f, "Named blocks", namedSizeInfo, false);
    		}

    		MultiPrintf(f, "\n-------------------- Stats sorted by file name and line number\n\n");

    		std::map<std::string, SizeInfo> FileMap;

    		{
    			SizeInfo FileAccum;
    			std::string lastFile;
    			int FileLines = 0;

    			SizeInfo totals;

    			std::map<FileLine, SizeInfo>::const_iterator i;
    			for (i = g_FileLineMap.begin(); i != g_FileLineMap.end(); ++i) {
    				if (lastFile != i->first.name) {
    					if (FileLines > 0) {
    						FileMap[lastFile] = FileAccum;
    					}
    					FileAccum = SizeInfo();
    					lastFile = i->first.name;
    					FileLines = 0;
    				}

    				char header[4096];
    				sprintf(header, "%s(%d)", i->first.name.c_str(), i->first.line);
    				DumpSingleBlock(f, header, i->second, false);
    				++FileLines;
    				FileAccum += i->second;
    				totals += i->second;
    			}

    			if (FileLines > 0) {
    				FileMap[lastFile] = FileAccum;
    			}

    			if (totals.totalBlocks > 0) {
    				MultiPrintf(f, "\nTotal %d blocks, %d bytes, %.2f avg bytes/block\n\n", totals.totalBlocks, totals.totalSize, (float)totals.totalSize / totals.totalBlocks);
    			} else {
    				MultiPrintf(f, "\nTotal 0 blocks, 0 bytes, 0 avg bytes/block\n\n");
    			}
    		}

    		{
    			MultiPrintf(f, "\n--------- Formatted\n\n");
    			MultiPrintf(f, "%-75s : blocks |    bytes | avg bytes/block\n", " ");
    			MultiPrintf(f, "%-75s-:--------|----------|----------------\n", " ");

    			SizeInfo totals;

    			std::map<FileLine, SizeInfo>::const_iterator i;
    			for (i = g_FileLineMap.begin(); i != g_FileLineMap.end(); ++i) {
    				char header[4096];
    				sprintf(header, "%-68s(%5d)", i->first.name.c_str(), i->first.line);
    				DumpSingleBlock(f, header, i->second, true);
    				totals += i->second;
    			}

    			if (totals.totalBlocks > 0) {
    				MultiPrintf(f, "\nTotal %d blocks, %d bytes, %.2f avg bytes/block\n\n", totals.totalBlocks, totals.totalSize, (float)totals.totalSize / totals.totalBlocks);
    			} else {
    				MultiPrintf(f, "\nTotal 0 blocks, 0 bytes, 0 avg bytes/block\n\n");
    			}
    		}

    		std::multimap<int, FileSizeInfo>	BlockFilesMap;

    		{
    			MultiPrintf(f, "\n-------------------- Stats sorted by file name\n\n");
    			MultiPrintf(f, "%-75s : blocks |    bytes | avg bytes/block\n", " ");
    			MultiPrintf(f, "%-75s-:--------|----------|----------------\n", " ");

    			SizeInfo totals;

    			std::map<std::string, SizeInfo>::const_iterator i;
    			for (i = FileMap.begin(); i != FileMap.end(); ++i) {
    				char header[4096];
    				sprintf(header, "%-75s", i->first.c_str());
    				DumpSingleBlock(f, header, i->second, true);
    				BlockFilesMap.insert(std::pair<const int, FileSizeInfo>(i->second.totalBlocks, FileSizeInfo(i->first, i->second)));
    				totals += i->second;
    			}

    			if (totals.totalBlocks > 0) {
    				MultiPrintf(f, "\nTotal %d blocks, %d bytes, %.2f avg bytes/block\n\n", totals.totalBlocks, totals.totalSize, (float)totals.totalSize / totals.totalBlocks);
    			} else {
    				MultiPrintf(f, "\nTotal 0 blocks, 0 bytes, 0 avg bytes/block\n\n");
    			}
    		}

    		std::multimap<int, FileSizeInfo>	SizeFilesMap;

    		{
    			MultiPrintf(f, "\n-------------------- File stats sorted by number of blocks\n\n");
    			MultiPrintf(f, "%-75s : blocks |    bytes | avg bytes/block\n", " ");
    			MultiPrintf(f, "%-75s-:--------|----------|----------------\n", " ");

    			SizeInfo totals;

    			std::multimap<int, FileSizeInfo>::const_iterator i;
    			for (i = BlockFilesMap.begin(); i != BlockFilesMap.end(); ++i) {
    				char header[4096];
    				sprintf(header, "%-75s", i->second.name.c_str());
    				DumpSingleBlock(f, header, i->second, true);
    				SizeFilesMap.insert(std::pair<const int, FileSizeInfo>(i->second.totalSize, i->second));
    				totals += i->second;
    			}

    			if (totals.totalBlocks > 0) {
    				MultiPrintf(f, "\nTotal %d blocks, %d bytes, %.2f avg bytes/block\n\n", totals.totalBlocks, totals.totalSize, (float)totals.totalSize / totals.totalBlocks);
    			} else {
    				MultiPrintf(f, "\nTotal 0 blocks, 0 bytes, 0 avg bytes/block\n\n");
    			}
    		}

    		std::map<std::string, SizeInfo> DirMap;

    		{
    			MultiPrintf(f, "\n-------------------- File stats sorted by total size\n\n");
    			MultiPrintf(f, "%-75s : blocks |    bytes | avg bytes/block\n", " ");
    			MultiPrintf(f, "%-75s-:--------|----------|----------------\n", " ");

    			SizeInfo totals;

    			std::multimap<int, FileSizeInfo>::const_iterator i;
    			for (i = SizeFilesMap.begin(); i != SizeFilesMap.end(); ++i) {
    				char header[4096];
    				sprintf(header, "%-75s", i->second.name.c_str());
    				DumpSingleBlock(f, header, i->second, true);
    				{
    					const char * const name = i->second.name.c_str();
    					const char *d = name + i->second.name.size()-1;
    					while (d > name && *d != '\\' && *d != '/') {
    						--d;
    					}
    					while (*d == '\\' || *d == '/') {
    						std::string dir(name, d-name);
    						std::map<std::string, SizeInfo>::iterator it = DirMap.find(dir);
    						if (it == DirMap.end()) {
    							DirMap.insert(std::pair<std::string, SizeInfo>(dir, i->second));
    						} else {
    							it->second += i->second;
    						}
    						--d;
    						while (d > name && *d != '\\' && *d != '/') {
    							--d;
    						}
    					}
    				}
    				totals += i->second;
    			}

    			if (totals.totalBlocks > 0) {
    				MultiPrintf(f, "\nTotal %d blocks, %d bytes, %.2f avg bytes/block\n\n", totals.totalBlocks, totals.totalSize, (float)totals.totalSize / totals.totalBlocks);
    			} else {
    				MultiPrintf(f, "\nTotal 0 blocks, 0 bytes, 0 avg bytes/block\n\n");
    			}
    		}

    		{
    			MultiPrintf(f, "\n-------------------- Directory stats\n\n");
    			MultiPrintf(f, "%-75s : blocks |    bytes | avg bytes/block\n", " ");
    			MultiPrintf(f, "%-75s-:--------|----------|----------------\n", " ");

    			std::map<std::string, SizeInfo>::const_iterator i;
    			for (i = DirMap.begin(); i != DirMap.end(); ++i) {
    				char header[4096];
    				sprintf(header, "%-75s", i->first.c_str());
    				DumpSingleBlock(f, header, i->second, true);
    			}
    		}

    		MultiPrintf(f, "\n-------------------- End DumpMemStats\n\n");

    		_CrtMemState dif;
    		if (&snap) {
    			_CrtMemState cur;
    			_CrtMemCheckpoint(&cur);
    			_CrtMemDifference(&dif, &snap.memState, &cur);
    		} else {
    			_CrtMemCheckpoint(&dif);
    		}
    		g_Fileo = f;
    		_CrtSetReportHook(OutputReportHook);
    		_CrtMemDumpStatistics(&dif);
    		_CrtSetReportHook(g_OldReportHook);
    		g_Fileo = NULL;

    		memset(&g_UnnamedSizeInfo,  0, sizeof(g_UnnamedSizeInfo));
    		memset(&g_TotalSizeInfo, 0, sizeof(g_TotalSizeInfo));
    		g_LastFName[0] = 0;
    		g_FileLineMap.clear();
    	}

    	ClearFileData();

    #else
    	MultiPrintf(f, "-------------------- Executed from Release mode, so no debug heap stats\n\n");
    #endif

    	DumpProcessInfo(f);
    	DumpGlobalInfo(f);

    	fclose(f);
    }

    void DumpShortMemStats(const char *file, unsigned int line)
    {
    	FILE *f = OpenLogFile();

    	if (file) {
    		MultiPrintf(f, "\n%s(%d): Code line that executed the short dump\n\n", file, line);
    	} else {
    		MultiPrintf(f, "\nPlease, use the macros to execute the short dump!!\n\n");
    	}

    	DumpGlobalInfo(f);

    	fclose(f);
    }

    void DumpMemStats(const char *file, unsigned int line)
    {
    	DumpMemStats(*(MemDebugSnapshot *)0, file, line);
    }

The zip file viewer built into the Developer Toolbox made use of the zlib library, as well as the zlibdll source additions.

Copyright 1999-2008 (C) FLIPCODE.COM and/or the original content author(s). All rights reserved.
Please read our Terms, Conditions, and Privacy information.