/*////////////////////////////////////////////////////////////////////////////
* Project:
* Memory_and_Exception_Trace
*
* ///////////////////////////////////////////////////////////////////////////
* File:
* Stackwalker.cpp
*
* Remarks:
* Dumps memory leaks (unreleased allocations) for CRT-Allocs and COM-Allocs
* Dumps the stack of an thread if an exepction occurs
*
* Known bugs:
* - If the allocation-RequestID wrap, then allocations will get lost...
*
* Author:
* Jochen Kalmbach, Germany
* (c) 2002-2005 (Freeware)
* http://www.codeproject.com/tools/leakfinder.asp
*
* License (The zlib/libpng License, http://www.opensource.org/licenses/zlib-license.php):
*
* Copyright (c) 2005 Jochen Kalmbach
*
* This software is provided 'as-is', without any express or implied warranty.
* In no event will the authors be held liable for any damages arising from the
* use of this software.
*
* Permission is granted to anyone to use this software for any purpose, including
* commercial applications, and to alter it and redistribute it freely, subject to
* the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not claim
* that you wrote the original software. If you use this software in a product,
* an acknowledgment in the product documentation would be appreciated but is
* not required.
*
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source distribution.
*
*//////////////////////////////////////////////////////////////////////////////
//#include "stdafx.h" // should be uncommented for precompiled headers
#include <windows.h>
#include <string>
#include <vector>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <crtdbg.h>
#include <tchar.h>
#include "Stackwalker.h"
// If the following is defined, only the used memories are stored in the hash-table.
// If the memory is freed, it will be removed from the hash-table (to reduce memory)
// Consequences: At DeInitAllocHook, only Leaks will be reported
#define HASH_ENTRY_REMOVE_AT_FREE
// 0 = Do not write any output during runtime-alloc-call
// 1 = Write only the alloc action (malloc, realloc, free)
// 2 = Write alloc action and callstack only for malloc/realloc
// 3 = Write alloc action and callstack for all actions
static ULONG g_ulShowStackAtAlloc = 0;
// the form of the output file
static eAllocCheckOutput g_CallstackOutputType = ACOutput_Simple;
// Size of Hash-Table (this should be a prime number to avoid collisions)
#define ALLOC_HASH_ENTRIES 1023
// Size of Callstack-trace in bytes (0x500 => appr. 5-9 functions, depending on parameter count for each function)
#define MAX_ESP_LEN_BUF 0x500
// Normally we can ignore allocations from the Runtime-System
#define IGNORE_CRT_ALLOC
// MaxSize: 1 MByte (only for StackwalkFilter)
#define LOG_FILE_MAX_SIZE 1024*1024
// If the following is defined, then COM-Leaks will also be tracked
#define WITH_IMALLOC_SPY
// #############################################################################################
#ifdef WITH_IMALLOC_SPY
//forwards:
void IMallocHashInsert(void *pData, CONTEXT &Context, size_t nDataSize);
BOOL IMallocHashRemove(void *pData);
// IMallocSpy-Interface
class CMallocSpy : public IMallocSpy
{
public:
CMallocSpy(void) {
m_cbRequest = 0;
}
~CMallocSpy(void) {
}
// IUnknown methods
STDMETHOD(QueryInterface) (REFIID riid, LPVOID *ppUnk) {
HRESULT hr = S_OK;
if (IsEqualIID(riid, IID_IUnknown)) {
*ppUnk = (IUnknown *) this;
}
else if (IsEqualIID(riid, IID_IMallocSpy)) {
*ppUnk = (IMalloc *) this;
}
else {
*ppUnk = NULL;
hr = E_NOINTERFACE;
}
AddRef();
return hr;
}
STDMETHOD_(ULONG, AddRef) (void) {
return InterlockedIncrement(&m_cRef);
}
STDMETHOD_(ULONG, Release) (void) {
LONG cRef;
cRef = InterlockedDecrement(&m_cRef);
if (cRef == 0)
{
delete this;
}
return cRef;
}
// IMallocSpy methods
STDMETHOD_(ULONG, PreAlloc) (ULONG cbRequest) {
m_cbRequest = cbRequest;
return cbRequest;
}
STDMETHOD_(void *, PostAlloc) (void *pActual) {
HANDLE hThread;
if (DuplicateHandle( GetCurrentProcess(), GetCurrentThread(),
GetCurrentProcess(), &hThread, 0, false, DUPLICATE_SAME_ACCESS ) != 0) {
// Ok
CONTEXT c;
memset( &c, '\0', sizeof c );
c.ContextFlags = CONTEXT_FULL;
#if 0
if ( GetThreadContext(hThread, &c) != 0) {
#else
__asm
{
call x
x: pop eax
mov c.Eip, eax
mov c.Ebp, ebp
}
{
#endif
// Ok
IMallocHashInsert(pActual, c, m_cbRequest);
}
CloseHandle(hThread);
}
return pActual;
}
STDMETHOD_(void *, PreFree) (void *pRequest, BOOL fSpyed) {
IMallocHashRemove(pRequest);
return pRequest;
}
STDMETHOD_(void, PostFree) (BOOL fSpyed) {
return;
}
STDMETHOD_(ULONG, PreRealloc) (void *pRequest, ULONG cbRequest,
void **ppNewRequest, BOOL fSpyed) {
IMallocHashRemove(pRequest);
m_cbRequest = cbRequest;
*ppNewRequest = pRequest; // Bug fixed. Thanx to Christoph Weber
return cbRequest;
}
STDMETHOD_(void *, PostRealloc) (void *pActual, BOOL fSpyed) {
HANDLE hThread;
if (DuplicateHandle( GetCurrentProcess(), GetCurrentThread(),
GetCurrentProcess(), &hThread, 0, false, DUPLICATE_SAME_ACCESS ) != 0) {
// Ok
CONTEXT c;
memset( &c, '\0', sizeof c );
c.ContextFlags = CONTEXT_FULL;
#if 0
if ( GetThreadContext(hThread, &c) != 0) {
#else
__asm
{
call x
x: pop eax
mov c.Eip, eax
mov c.Ebp, ebp
}
{
#endif
// Ok
IMallocHashInsert(pActual, c, m_cbRequest);
}
CloseHandle(hThread);
}
return pActual;
}
STDMETHOD_(void *, PreGetSize) (void *pRequest, BOOL fSpyed) {
return pRequest;
}
STDMETHOD_(ULONG, PostGetSize) (ULONG cbActual, BOOL fSpyed) {
return cbActual;
}
STDMETHOD_(void *, PreDidAlloc) (void *pRequest, BOOL fSpyed) {
return pRequest;
}
STDMETHOD_(BOOL, PostDidAlloc) (void *pRequest, BOOL fSpyed, BOOL fActual) {
return fActual;
}
STDMETHOD_(void, PreHeapMinimize) (void) {
return;
}
STDMETHOD_(void, PostHeapMinimize) (void) {
return;
}
private:
LONG m_cRef;
ULONG m_cbRequest;
};
#endif
// #############################################################################################
// Here I have included the API-Version 9 declarations, so it will also compile on systems, where the new PSDK is not installed
// Normally we just need to include the "dbghelp.h" file
#include <imagehlp.h>
#if API_VERSION_NUMBER < 9
typedef
BOOL
(__stdcall *PREAD_PROCESS_MEMORY_ROUTINE64)(
HANDLE hProcess,
DWORD64 qwBaseAddress,
PVOID lpBuffer,
DWORD nSize,
LPDWORD lpNumberOfBytesRead
);
typedef struct _IMAGEHLP_LINE64 {
DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_LINE64)
PVOID Key; // internal
DWORD LineNumber; // line number in file
PCHAR FileName; // full filename
DWORD64 Address; // first instruction of line
} IMAGEHLP_LINE64, *PIMAGEHLP_LINE64;
typedef struct _IMAGEHLP_MODULE64 {
DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_MODULE64)
DWORD64 BaseOfImage;