-/*////////////////////////////////////////////////////////////////////////////\r
- * Project:\r
- * Memory_and_Exception_Trace\r
- *\r
- * ///////////////////////////////////////////////////////////////////////////\r
- * File:\r
- * Stackwalker.cpp\r
- *\r
- * Remarks:\r
- * Dumps memory leaks (unreleased allocations) for CRT-Allocs and COM-Allocs\r
- * Dumps the stack of an thread if an exepction occurs\r
- *\r
- * Known bugs:\r
- * - If the allocation-RequestID wrap, then allocations will get lost...\r
- *\r
- * Author:\r
- * Jochen Kalmbach, Germany\r
- * (c) 2002-2005 (Freeware)\r
- * http://www.codeproject.com/tools/leakfinder.asp\r
- * \r
- * License (The zlib/libpng License, http://www.opensource.org/licenses/zlib-license.php):\r
- *\r
- * Copyright (c) 2005 Jochen Kalmbach\r
- *\r
- * This software is provided 'as-is', without any express or implied warranty. \r
- * In no event will the authors be held liable for any damages arising from the \r
- * use of this software.\r
- *\r
- * Permission is granted to anyone to use this software for any purpose, including \r
- * commercial applications, and to alter it and redistribute it freely, subject to \r
- * the following restrictions:\r
- * \r
- * 1. The origin of this software must not be misrepresented; you must not claim \r
- * that you wrote the original software. If you use this software in a product, \r
- * an acknowledgment in the product documentation would be appreciated but is \r
- * not required.\r
- *\r
- * 2. Altered source versions must be plainly marked as such, and must not be \r
- * misrepresented as being the original software.\r
- *\r
- * 3. This notice may not be removed or altered from any source distribution.\r
- *\r
- *//////////////////////////////////////////////////////////////////////////////\r
-\r
-//#include "stdafx.h" // should be uncommented for precompiled headers\r
-\r
-#include <windows.h>\r
-#include <string>\r
-#include <vector>\r
-#include <stdio.h>\r
-#include <stdlib.h>\r
-#include <time.h>\r
-#include <crtdbg.h>\r
-#include <tchar.h>\r
-\r
-#include "Stackwalker.h"\r
-\r
-#pragma warning(push)\r
-#pragma warning(disable : 4100)\r
-#pragma warning(disable : 4996)\r
-#pragma warning(disable : 4189)\r
-#pragma warning(disable : 4245)\r
-#pragma warning(disable : 4701)\r
-\r
-// If the following is defined, only the used memories are stored in the hash-table. \r
-// If the memory is freed, it will be removed from the hash-table (to reduce memory)\r
-// Consequences: At DeInitAllocHook, only Leaks will be reported\r
-#define HASH_ENTRY_REMOVE_AT_FREE\r
-\r
-\r
-// 0 = Do not write any output during runtime-alloc-call\r
-// 1 = Write only the alloc action (malloc, realloc, free)\r
-// 2 = Write alloc action and callstack only for malloc/realloc\r
-// 3 = Write alloc action and callstack for all actions\r
-static ULONG g_ulShowStackAtAlloc = 0;\r
-\r
-// the form of the output file\r
-static eAllocCheckOutput g_CallstackOutputType = ACOutput_Simple;\r
-\r
-\r
-// Size of Hash-Table (this should be a prime number to avoid collisions)\r
-#define ALLOC_HASH_ENTRIES 1023\r
-\r
-\r
-// Size of Callstack-trace in bytes (0x500 => appr. 5-9 functions, depending on parameter count for each function)\r
-#define MAX_ESP_LEN_BUF 0x500\r
-\r
-\r
-// Normally we can ignore allocations from the Runtime-System\r
-#define IGNORE_CRT_ALLOC\r
-\r
-// MaxSize: 1 MByte (only for StackwalkFilter)\r
-#define LOG_FILE_MAX_SIZE 1024*1024\r
-\r
-// If the following is defined, then COM-Leaks will also be tracked\r
-#define WITH_IMALLOC_SPY\r
-\r
-\r
-// #############################################################################################\r
-#ifdef WITH_IMALLOC_SPY\r
-//forwards:\r
-void IMallocHashInsert(void *pData, CONTEXT &Context, size_t nDataSize);\r
-BOOL IMallocHashRemove(void *pData);\r
-\r
-// IMallocSpy-Interface\r
-class CMallocSpy : public IMallocSpy\r
-{\r
-public:\r
- CMallocSpy(void) {\r
- m_cbRequest = 0;\r
- }\r
- ~CMallocSpy(void) {\r
- }\r
- // IUnknown methods\r
- STDMETHOD(QueryInterface) (REFIID riid, LPVOID *ppUnk) {\r
- HRESULT hr = S_OK;\r
- if (IsEqualIID(riid, IID_IUnknown)) {\r
- *ppUnk = (IUnknown *) this;\r
- }\r
- else if (IsEqualIID(riid, IID_IMallocSpy)) {\r
- *ppUnk = (IMalloc *) this;\r
- }\r
- else {\r
- *ppUnk = NULL;\r
- hr = E_NOINTERFACE;\r
- }\r
- AddRef();\r
- return hr;\r
- }\r
- STDMETHOD_(ULONG, AddRef) (void) {\r
- return InterlockedIncrement(&m_cRef);\r
- }\r
- STDMETHOD_(ULONG, Release) (void) {\r
- LONG cRef;\r
- cRef = InterlockedDecrement(&m_cRef);\r
- if (cRef == 0)\r
- {\r
- delete this;\r
- }\r
- return cRef;\r
- }\r
- // IMallocSpy methods\r
- STDMETHOD_(ULONG, PreAlloc) (ULONG cbRequest) {\r
- m_cbRequest = cbRequest;\r
- return cbRequest;\r
- }\r
- STDMETHOD_(void *, PostAlloc) (void *pActual) {\r
- HANDLE hThread;\r
- if (DuplicateHandle( GetCurrentProcess(), GetCurrentThread(),\r
- GetCurrentProcess(), &hThread, 0, false, DUPLICATE_SAME_ACCESS ) != 0) {\r
- // Ok\r
- CONTEXT c;\r
- memset( &c, '\0', sizeof c );\r
- c.ContextFlags = CONTEXT_FULL;\r
-#if 0\r
- if ( GetThreadContext(hThread, &c) != 0) {\r
-#else\r
- __asm\r
- {\r
- call x\r
- x: pop eax\r
- mov c.Eip, eax\r
- mov c.Ebp, ebp\r
- }\r
- {\r
-#endif\r
- // Ok\r
- IMallocHashInsert(pActual, c, m_cbRequest);\r
- }\r
- CloseHandle(hThread);\r
- }\r
- return pActual;\r
- }\r
- STDMETHOD_(void *, PreFree) (void *pRequest, BOOL fSpyed) {\r
- IMallocHashRemove(pRequest);\r
- return pRequest;\r
- }\r
- STDMETHOD_(void, PostFree) (BOOL fSpyed) {\r
- return;\r
- }\r
- STDMETHOD_(ULONG, PreRealloc) (void *pRequest, ULONG cbRequest,\r
- void **ppNewRequest, BOOL fSpyed) {\r
- IMallocHashRemove(pRequest);\r
- m_cbRequest = cbRequest;\r
- *ppNewRequest = pRequest; // Bug fixed. Thanx to Christoph Weber\r
- return cbRequest;\r
- }\r
- STDMETHOD_(void *, PostRealloc) (void *pActual, BOOL fSpyed) {\r
- HANDLE hThread;\r
- if (DuplicateHandle( GetCurrentProcess(), GetCurrentThread(),\r
- GetCurrentProcess(), &hThread, 0, false, DUPLICATE_SAME_ACCESS ) != 0) {\r
- // Ok\r
- CONTEXT c;\r
- memset( &c, '\0', sizeof c );\r
- c.ContextFlags = CONTEXT_FULL;\r
-#if 0\r
- if ( GetThreadContext(hThread, &c) != 0) {\r
-#else\r
- __asm\r
- {\r
- call x\r
- x: pop eax\r
- mov c.Eip, eax\r
- mov c.Ebp, ebp\r
- }\r
- {\r
-#endif\r
- // Ok\r
- IMallocHashInsert(pActual, c, m_cbRequest);\r
- }\r
- CloseHandle(hThread);\r
- }\r
- return pActual;\r
- }\r
- STDMETHOD_(void *, PreGetSize) (void *pRequest, BOOL fSpyed) {\r
- return pRequest;\r
- }\r
- STDMETHOD_(ULONG, PostGetSize) (ULONG cbActual, BOOL fSpyed) {\r
- return cbActual;\r
- }\r
- STDMETHOD_(void *, PreDidAlloc) (void *pRequest, BOOL fSpyed) {\r
- return pRequest;\r
- }\r
- STDMETHOD_(BOOL, PostDidAlloc) (void *pRequest, BOOL fSpyed, BOOL fActual) {\r
- return fActual;\r
- }\r
- STDMETHOD_(void, PreHeapMinimize) (void) {\r
- return;\r
- }\r
- STDMETHOD_(void, PostHeapMinimize) (void) {\r
- return;\r
- }\r
-private:\r
- LONG m_cRef;\r
- ULONG m_cbRequest;\r
-};\r
-#endif\r
-\r
-// #############################################################################################\r
-// Here I have included the API-Version 9 declarations, so it will also compile on systems, where the new PSDK is not installed\r
-// Normally we just need to include the "dbghelp.h" file\r
-#include <imagehlp.h>\r
-#if API_VERSION_NUMBER < 9\r
-typedef\r
-BOOL\r
-(__stdcall *PREAD_PROCESS_MEMORY_ROUTINE64)(\r
- HANDLE hProcess,\r
- DWORD64 qwBaseAddress,\r
- PVOID lpBuffer,\r
- DWORD nSize,\r
- LPDWORD lpNumberOfBytesRead\r
- );\r
-\r
-typedef struct _IMAGEHLP_LINE64 {\r
- DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_LINE64)\r
- PVOID Key; // internal\r
- DWORD LineNumber; // line number in file\r
- PCHAR FileName; // full filename\r
- DWORD64 Address; // first instruction of line\r
-} IMAGEHLP_LINE64, *PIMAGEHLP_LINE64;\r
-\r
-\r
-typedef struct _IMAGEHLP_MODULE64 {\r
- DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_MODULE64)\r
- DWORD64 BaseOfImage; // base load address of module\r
- DWORD ImageSize; // virtual size of the loaded module\r
- DWORD TimeDateStamp; // date/time stamp from pe header\r
- DWORD CheckSum; // checksum from the pe header\r
- DWORD NumSyms; // number of symbols in the symbol table\r
- SYM_TYPE SymType; // type of symbols loaded\r
- CHAR ModuleName[32]; // module name\r
- CHAR ImageName[256]; // image name\r
- CHAR LoadedImageName[256]; // symbol file name\r
-} IMAGEHLP_MODULE64, *PIMAGEHLP_MODULE64;\r
-\r
-typedef struct _IMAGEHLP_SYMBOL64 {\r
- DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_SYMBOL64)\r
- DWORD64 Address; // virtual address including dll base address\r
- DWORD Size; // estimated size of symbol, can be zero\r
- DWORD Flags; // info about the symbols, see the SYMF defines\r
- DWORD MaxNameLength; // maximum size of symbol name in 'Name'\r
- CHAR Name[1]; // symbol name (null terminated string)\r
-} IMAGEHLP_SYMBOL64, *PIMAGEHLP_SYMBOL64;\r
-\r
-typedef struct _tagADDRESS64 {\r
- DWORD64 Offset;\r
- WORD Segment;\r
- ADDRESS_MODE Mode;\r
-} ADDRESS64, *LPADDRESS64;\r
-\r
-typedef struct _KDHELP64 {\r
-\r
- //\r
- // address of kernel thread object, as provided in the\r
- // WAIT_STATE_CHANGE packet.\r
- //\r
- DWORD64 Thread;\r
-\r
- //\r
- // offset in thread object to pointer to the current callback frame\r
- // in kernel stack.\r
- //\r
- DWORD ThCallbackStack;\r
-\r
- //\r
- // offset in thread object to pointer to the current callback backing\r
- // store frame in kernel stack.\r
- //\r
- DWORD ThCallbackBStore;\r
-\r
- //\r
- // offsets to values in frame:\r
- //\r
- // address of next callback frame\r
- DWORD NextCallback;\r
-\r
- // address of saved frame pointer (if applicable)\r
- DWORD FramePointer;\r
-\r
-\r
- //\r
- // Address of the kernel function that calls out to user mode\r
- //\r
- DWORD64 KiCallUserMode;\r
-\r
- //\r
- // Address of the user mode dispatcher function\r
- //\r
- DWORD64 KeUserCallbackDispatcher;\r
-\r
- //\r
- // Lowest kernel mode address\r
- //\r
- DWORD64 SystemRangeStart;\r
-\r
- DWORD64 Reserved[8];\r
-\r
-} KDHELP64, *PKDHELP64;\r
-\r
-\r
-typedef struct _tagSTACKFRAME64 {\r
- ADDRESS64 AddrPC; // program counter\r
- ADDRESS64 AddrReturn; // return address\r
- ADDRESS64 AddrFrame; // frame pointer\r
- ADDRESS64 AddrStack; // stack pointer\r
- ADDRESS64 AddrBStore; // backing store pointer\r
- PVOID FuncTableEntry; // pointer to pdata/fpo or NULL\r
- DWORD64 Params[4]; // possible arguments to the function\r
- BOOL Far; // WOW far call\r
- BOOL Virtual; // is this a virtual frame?\r
- DWORD64 Reserved[3];\r
- KDHELP64 KdHelp;\r
-} STACKFRAME64, *LPSTACKFRAME64;\r
-\r
-typedef\r
-PVOID\r
-(__stdcall *PFUNCTION_TABLE_ACCESS_ROUTINE64)(\r
- HANDLE hProcess,\r
- DWORD64 AddrBase\r
- );\r
-\r
-typedef\r
-DWORD64\r
-(__stdcall *PGET_MODULE_BASE_ROUTINE64)(\r
- HANDLE hProcess,\r
- DWORD64 Address\r
- );\r
-\r
-typedef\r
-DWORD64\r
-(__stdcall *PTRANSLATE_ADDRESS_ROUTINE64)(\r
- HANDLE hProcess,\r
- HANDLE hThread,\r
- LPADDRESS64 lpaddr\r
- );\r
-#endif\r
-// #############################################################################################\r
-\r
-\r
-\r
-// Forward definitions of functions:\r
-static void ShowStackRM( HANDLE hThread, CONTEXT& c, FILE *fLogFile, PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryFunction, HANDLE hProcess);\r
-static void ShowStack( HANDLE hThread, CONTEXT& c, FILE *fLogFile);\r
-\r
-//static void AllocHashOut(FILE*);\r
-static ULONG AllocHashOutLeaks(FILE*);\r
-\r
-\r
-\r
-// Globale Vars:\r
-static TCHAR *g_pszAllocLogName = NULL;\r
-static FILE *g_fFile = NULL;\r
-\r
-// AllocCheckFileOpen\r
-// Checks if the log-file is already opened\r
-// if not, try to open file (append or create if not exists)\r
-// if open failed, redirect output to stdout\r
-static void AllocCheckFileOpen(bool bAppend = true) {\r
- // is the File already open? If not open it...\r
- if (g_fFile == NULL)\r
- if (g_pszAllocLogName != NULL)\r
- {\r
- if (bAppend == false)\r
- g_fFile = _tfopen(g_pszAllocLogName, _T("w"));\r
- else\r
- g_fFile = _tfopen(g_pszAllocLogName, _T("a"));\r
- }\r
- if (g_fFile == NULL)\r
- g_fFile = stdout;\r
-}\r
-\r
-// Write Date/Time to specified file (will also work after 2038)\r
-static void WriteDateTime(FILE *fFile, BOOL asXMLAttrs = FALSE) {\r
- TCHAR pszTemp[11], pszTemp2[11];\r
-\r
- if (fFile != NULL) {\r
- _tstrdate( pszTemp );\r
- _tstrtime( pszTemp2 );\r
- if (asXMLAttrs == FALSE)\r
- _ftprintf(fFile, _T("%s %s"), pszTemp, pszTemp2 ); // also ok after year 2038 (asctime is NOT ok)\r
- else\r
- _ftprintf(fFile, _T("date=\"%s\" time=\"%s\" "), pszTemp, pszTemp2 ); // also ok after year 2038 (asctime is NOT ok)\r
- }\r
-} // WriteDateTime\r
-\r
-\r
-/*******************************************************************************\r
- * Hash-Tabelle\r
- *******************************************************************************/\r
-// Memory for the EIP-Address (is used by the ShowStack-method)\r
-#define MAX_EIP_LEN_BUF 4\r
-\r
-#define ALLOC_ENTRY_NOT_FOUND 0xFFFFFFFF\r
-\r
-typedef struct AllocHashEntryType {\r
- long lRequestID; // RequestID from CRT (if 0, then this entry is empty)\r
- size_t nDataSize; // Size of the allocated memory\r
- char cRemovedFlag; // 0 => memory was not yet released\r
- struct AllocHashEntryType *Next;\r
- // Callstack for EIP\r
- DWORD dwEIPOffset;\r
- DWORD dwEIPLen;\r
- char pcEIPAddr[MAX_EIP_LEN_BUF];\r
- // Callstack for ESP\r
- DWORD dwESPOffset;\r
- DWORD dwESPLen;\r
- char pcESPAddr[MAX_ESP_LEN_BUF];\r
-} AllocHashEntryType;\r
-\r
-static AllocHashEntryType AllocHashTable[ALLOC_HASH_ENTRIES];\r
-static ULONG AllocHashEntries = 0;\r
-static ULONG AllocHashCollisions = 0;\r
-static ULONG AllocHashFreed = 0;\r
-static ULONG AllocHashMaxUsed = 0; // maximal number of concurrent entries\r
-static ULONG AllocHashCurrentCount = 0;\r
-\r
-static ULONG AllocHashMaxCollisions = 0;\r
-static ULONG AllocHashCurrentCollisions = 0;\r
-\r
-// ##########################################################################################\r
-#ifdef WITH_IMALLOC_SPY\r
-// eigene Tabelle für die IMallocs:\r
-typedef struct IMallocHashEntryType {\r
- void *pData; // Key-Word\r
- size_t nDataSize; // größe des Datenblocks (optional)\r
- char cRemovedFlag; // 0 => nicht wurde noch nicht freigegeben\r
- struct IMallocHashEntryType *Next;\r
- // Callstack für EIP\r
- DWORD dwEIPOffset;\r
- DWORD dwEIPLen;\r
- char pcEIPAddr[MAX_EIP_LEN_BUF];\r
- // Callstack für ESP\r
- DWORD dwESPOffset;\r
- DWORD dwESPLen;\r
- char pcESPAddr[MAX_ESP_LEN_BUF];\r
-} IMallocHashEntryType;\r
-\r
-static IMallocHashEntryType IMallocHashTable[ALLOC_HASH_ENTRIES];\r
-\r
-static ULONG IMallocHashEntries = 0;\r
-static ULONG IMallocHashCollisions = 0;\r
-static ULONG IMallocHashFreed = 0;\r
-static ULONG IMallocHashMaxUsed = 0; // maximal number of concurrent entries\r
-static ULONG IMallocHashCurrentCount = 0;\r
-\r
-static ULONG IMallocHashMaxCollisions = 0;\r
-static ULONG IMallocHashCurrentCollisions = 0;\r
-\r
-\r
-//static void AllocHashOut(FILE*);\r
-static ULONG IMallocHashOutLeaks(FILE*);\r
-\r
-// AllocHashFunction\r
-// Die eigentliche Hash-Funktion (hier ganz simpel)\r
-static ULONG IMallocHashFunction(void *pData) {\r
- ULONG ulTemp;\r
- DWORD dwPointer = (DWORD) pData;\r
-\r
- // relativ simpler Mechanismus für die Hash-Funktion,\r
- // mir ist nur nix besseres eingefallen...\r
- ulTemp = dwPointer % ALLOC_HASH_ENTRIES;\r
-\r
- _ASSERTE( (ulTemp >= 0) && (ulTemp < ALLOC_HASH_ENTRIES) );\r
-\r
- return ulTemp;\r
-} // AllocHashFunction\r
-\r
-// IMallocHashInsert\r
-// pData: Key-Word (Pointer to address)\r
-// pContext: Context-Record, for retrieving Callstack (EIP and EBP is only needed)\r
-// nDataSize: How many bytes\r
-void IMallocHashInsert(void *pData, CONTEXT &Context, size_t nDataSize) {\r
- ULONG HashIdx;\r
- IMallocHashEntryType *pHashEntry;\r
-\r
- // ermittle Statistische Werte\r
- IMallocHashEntries++;\r
- IMallocHashCurrentCount++;\r
- if (IMallocHashCurrentCount > IMallocHashMaxUsed)\r
- IMallocHashMaxUsed = IMallocHashCurrentCount;\r
-\r
- // ermittle den Hash-Wert\r
- HashIdx = IMallocHashFunction(pData);\r
-\r
- // Eintrag darf nicht größer als die Hash-Tabelle sein\r
- _ASSERTE(HashIdx < ALLOC_HASH_ENTRIES);\r
-\r
- pHashEntry = &IMallocHashTable[HashIdx];\r
- if (pHashEntry->pData == 0) {\r
- // es ist noch kein Eintrag da\r
- }\r
- else {\r
- //Statistische Daten:\r
- IMallocHashCollisions++;\r
- IMallocHashCurrentCollisions++;\r
- if (IMallocHashCurrentCollisions > IMallocHashMaxCollisions)\r
- IMallocHashMaxCollisions = IMallocHashCurrentCollisions;\r
-\r
- // Eintrag ist schon belegt, verkette die Einträge\r
- // wenn dies oft vorkommt, sollte man entweder die Tabelle vergrößern oder eine\r
- // andere Hash-Funktion wählen\r
- while(pHashEntry->Next != NULL) {\r
- pHashEntry = pHashEntry->Next;\r
- }\r
-\r
- pHashEntry->Next = (IMallocHashEntryType*) _calloc_dbg(sizeof(IMallocHashEntryType), 1, _CRT_BLOCK, __FILE__, __LINE__);\r
- pHashEntry = pHashEntry->Next;\r
-\r
- }\r
- pHashEntry->pData = pData; // Key-Word\r
- pHashEntry->nDataSize = nDataSize;\r
- pHashEntry->Next = NULL;\r
- // Get EIP and save it in the record\r
- pHashEntry->dwEIPOffset = Context.Eip;\r
- if (ReadProcessMemory(GetCurrentProcess(), (LPCVOID) Context.Eip, &(pHashEntry->pcEIPAddr), MAX_EIP_LEN_BUF, &(pHashEntry->dwEIPLen)) == 0) {\r
- // Could not read memory... remove everything...\r
- memset(pHashEntry->pcEIPAddr, 0, MAX_EIP_LEN_BUF);\r
- pHashEntry->dwEIPLen = 0;\r
- pHashEntry->dwEIPOffset = 0;\r
- }\r
-\r
- // Get ESP and save it in the record\r
- pHashEntry->dwESPOffset = Context.Ebp;\r
- if (ReadProcessMemory(GetCurrentProcess(), (LPCVOID) Context.Ebp, &(pHashEntry->pcESPAddr), MAX_ESP_LEN_BUF, &(pHashEntry->dwESPLen)) == 0) {\r
- // Could not read memory... remove everything...\r
- memset(pHashEntry->pcESPAddr, 0, MAX_ESP_LEN_BUF);\r
- pHashEntry->dwESPLen = 0;\r
- pHashEntry->dwESPOffset = 0;\r
-\r
- // Check if I tried to read too much...\r
- if (GetLastError() == ERROR_PARTIAL_COPY)\r
- {\r
- // ask how many I can read:\r
- MEMORY_BASIC_INFORMATION MemBuffer;\r
- DWORD dwRet = VirtualQuery((LPCVOID) Context.Ebp, &MemBuffer, sizeof(MemBuffer));\r
- if (dwRet > 0)\r
- {\r
- // calculate the length\r
- DWORD len = ((DWORD) MemBuffer.BaseAddress + MemBuffer.RegionSize) - Context.Ebp;\r
- if ( (len > 0) && (len < MAX_ESP_LEN_BUF) )\r
- {\r
- // try to read it again (with the shorter length)\r
- if (ReadProcessMemory(GetCurrentProcess(), (LPCVOID) Context.Ebp, &(pHashEntry->pcESPAddr), len, &(pHashEntry->dwESPLen)) == 0)\r
- {\r
- // ok, now everything goes wrong... remove it...\r
- memset(pHashEntry->pcESPAddr, 0, MAX_ESP_LEN_BUF);\r
- pHashEntry->dwESPLen = 0;\r
- pHashEntry->dwESPOffset = 0;\r
- }\r
- else\r
- {\r
- pHashEntry->dwESPOffset = Context.Ebp;\r
- }\r
- }\r
- } // VirtualQuery was successfully\r
- } // ERROR_PARTIAL_COPY\r
- }\r
-}\r
-\r
-// IMallocHashFind\r
-// Wird ALLOC_ENTRY_NOT_FOUND zurückgegeben, so wurde der Key nicht \r
-// gefunden, ansonsten wird ein Zeiger auf den Hash-Eintrag zurückgegeben\r
-// ACHTUNG: In einem preemptiven Tasking-System kann hier nicht \r
-// garantiert werden, ob der Zeiger noch gültig ist, wenn er \r
-// zurückgegeben wird, da er von einem anderen Thread schon\r
-// freigegeben sein könnte. \r
-// Die synchronisation muß eine Ebene höher erfolgen\r
-static IMallocHashEntryType *IMallocHashFind(void *pData) {\r
- ULONG HashIdx;\r
- IMallocHashEntryType *pHashEntry;\r
-\r
- // ermittle den Hash-Wert\r
- HashIdx = IMallocHashFunction(pData);\r
-\r
- // Eintrag darf nicht größer als die Hash-Tabelle sein\r
- _ASSERTE(HashIdx < ALLOC_HASH_ENTRIES);\r
-\r
- pHashEntry = &IMallocHashTable[HashIdx];\r
- while(pHashEntry != NULL) {\r
- if (pHashEntry->pData == pData) {\r
- return pHashEntry;\r
- }\r
- pHashEntry = pHashEntry->Next;\r
- }\r
-\r
- // wenn hier angelangt, dann wurde der Eintrag nicht gefunden!\r
- return (IMallocHashEntryType*) ALLOC_ENTRY_NOT_FOUND;\r
-} // AllocHashFind\r
-\r
-// IMallocHashRemove\r
-// Return: FALSE (0) : Key wurde gefunden und entfernt/markiert\r
-// TRUE (!=0): Key wurde nicht gefunden!\r
-BOOL IMallocHashRemove(void *pData) {\r
- ULONG HashIdx;\r
- IMallocHashEntryType *pHashEntry, *pHashEntryLast;\r
-\r
- // ermittle den Hash-Wert\r
- HashIdx = IMallocHashFunction(pData);\r
-\r
- // Eintrag darf nicht größer als die Hash-Tabelle sein\r
- _ASSERTE(HashIdx < ALLOC_HASH_ENTRIES);\r
-\r
- pHashEntryLast = NULL;\r
- pHashEntry = &IMallocHashTable[HashIdx];\r
- while(pHashEntry != NULL) {\r
- if (pHashEntry->pData == pData) {\r
-#ifdef HASH_ENTRY_REMOVE_AT_FREE\r
- IMallocHashFreed++;\r
- IMallocHashCurrentCount--;\r
- // gebe den Speicher frei\r
- if (pHashEntryLast == NULL) {\r
- // Es ist ein Eintrag direkt in der Tabelle\r
- if (pHashEntry->Next == NULL) {\r
- // Es ist der letze Eintrag lösche also die Tabelle\r
- memset(&IMallocHashTable[HashIdx], 0, sizeof(IMallocHashTable[HashIdx]));\r
- }\r
- else {\r
- // Es sind noch Einträge verkettet, überschreibe einfach den nicht mehr gebrauchten...\r
- IMallocHashEntryType *pTmp = pHashEntry->Next;\r
- *pHashEntry = *(pHashEntry->Next);\r
- _free_dbg(pTmp, _CRT_BLOCK);\r
- }\r
- return TRUE;\r
- }\r
- else {\r
- // ich bin in einem dynamischen Bereich\r
- // dies war eine kollisions, zähle also wieder zurück:\r
- IMallocHashCurrentCollisions--;\r
- pHashEntryLast->Next = pHashEntry->Next;\r
- _free_dbg(pHashEntry, _CRT_BLOCK);\r
- return TRUE;\r
- }\r
-#else\r
- // erhöhe nur den Removed counter und behalte das Object im Speicher\r
- pHashEntry->cRemovedFlag++;\r
- return TRUE; // erfolgreich\r
-#endif\r
- }\r
- pHashEntryLast = pHashEntry;\r
- pHashEntry = pHashEntry->Next;\r
- }\r
-\r
- // wenn hier angelangt, dann wurde der Eintrag nicht gefunden!\r
- return FALSE;\r
-}\r
-\r
-\r
-\r
-// Callback-Funtion for StackWalk für meine CallStack-Ausgabe aus der Hash-Tabelle\r
-static BOOL __stdcall ReadProcMemoryFromIMallocHash(HANDLE pData, DWORD64 lpBaseAddress, PVOID lpBuffer, DWORD nSize, PDWORD lpNumberOfBytesRead) {\r
- // Versuche die hRequestID zu finden\r
- IMallocHashEntryType *pHashEntry;\r
- *lpNumberOfBytesRead = 0;\r
-\r
- pHashEntry = IMallocHashFind((PVOID) pData);\r
- if (pHashEntry == (IMallocHashEntryType*) ALLOC_ENTRY_NOT_FOUND) {\r
- // nicht gefunden, somit kann ich den Speicher nicht lesen\r
- *lpNumberOfBytesRead = 0;\r
- return FALSE;\r
- }\r
- if ( ((DWORD) lpBaseAddress >= pHashEntry->dwESPOffset) && ((DWORD) lpBaseAddress <= (pHashEntry->dwESPOffset+pHashEntry->dwESPLen)) ) {\r
- // Speicher liegt im ESP:\r
- // Errechne den Offset\r
- DWORD dwOffset = (DWORD) lpBaseAddress - pHashEntry->dwESPOffset;\r
- DWORD dwSize = __min(nSize, MAX_ESP_LEN_BUF-dwOffset);\r
- memcpy(lpBuffer, &(pHashEntry->pcESPAddr[dwOffset]), dwSize);\r
- *lpNumberOfBytesRead = dwSize;\r
- if (dwSize != nSize)\r
- return FALSE;\r
- }\r
-\r
- if ( ((DWORD) lpBaseAddress >= pHashEntry->dwEIPOffset) && ((DWORD) lpBaseAddress <= (pHashEntry->dwEIPOffset+pHashEntry->dwEIPLen)) ) {\r
- // Speicher liegt im EIP:\r
- // Errechne den Offset\r
- DWORD dwOffset = (DWORD) lpBaseAddress - pHashEntry->dwEIPOffset;\r
- DWORD dwSize = __min(nSize, MAX_ESP_LEN_BUF-dwOffset);\r
- memcpy(lpBuffer, &(pHashEntry->pcEIPAddr[dwOffset]), dwSize);\r
- *lpNumberOfBytesRead = dwSize;\r
- if (dwSize != nSize)\r
- return FALSE;\r
- }\r
- \r
- if (*lpNumberOfBytesRead == 0) // Der Speicher konnte nicht gefunden werden\r
- return FALSE;\r
-\r
- return TRUE;\r
-}\r
-// AllocHashOutLeaks\r
-// Gibt allen Speicher aus, der noch nicht wieder freigegeben wurde\r
-// Returns the number of bytes, that are not freed (leaks)\r
-ULONG IMallocHashOutLeaks(FILE *fFile) {\r
- ULONG ulTemp;\r
- IMallocHashEntryType *pHashEntry;\r
- ULONG ulCount = 0;\r
- ULONG ulLeaksByte = 0;\r
-\r
- // Gehe jeden Eintrag durch und gebe ihn aus\r
- for(ulTemp = 0; ulTemp < ALLOC_HASH_ENTRIES; ulTemp++) {\r
- pHashEntry = &IMallocHashTable[ulTemp];\r
- if (pHashEntry->pData != 0) {\r
- while(pHashEntry != NULL) {\r
- // gebe die Zeile aus\r
- if ( (pHashEntry->cRemovedFlag <= 0) || (pHashEntry->cRemovedFlag > 1) ) {\r
- ulCount++;\r
- if (g_CallstackOutputType == ACOutput_XML)\r
- _ftprintf(fFile, _T("<LEAK requestID=\"%u\" size=\"%u\">\n"), pHashEntry->pData, pHashEntry->nDataSize);\r
- else\r
- _ftprintf(fFile, _T("Pointer (RequestID): %12i, Removed: %i, Size: %12i\n"), pHashEntry->pData, pHashEntry->cRemovedFlag, pHashEntry->nDataSize);\r
- CONTEXT c;\r
- memset( &c, '\0', sizeof c );\r
- c.Eip = pHashEntry->dwEIPOffset;\r
- c.Ebp = pHashEntry->dwESPOffset;\r
- ShowStackRM( NULL, c, fFile, &ReadProcMemoryFromIMallocHash, (HANDLE) pHashEntry->pData);\r
- // Zähle zusammen wieviel Byte noch nicht freigegeben wurden\r
- if (pHashEntry->nDataSize > 0)\r
- ulLeaksByte += pHashEntry->nDataSize;\r
- else\r
- ulLeaksByte++; // Wenn zwar Speicher allokiert wurde, dieser aber 0 Bytes lang war, so reserviere für diesen zumindest 1 Byte\r
-\r
- if (g_CallstackOutputType == ACOutput_XML)\r
- _ftprintf(fFile, _T("</LEAK>\n")); // terminate the xml-node\r
- }\r
- pHashEntry = pHashEntry->Next;\r
- }\r
- }\r
- }\r
- if (g_CallstackOutputType != ACOutput_XML)\r
- _ftprintf(fFile, _T("\n**** Number of leaks: %i\n"), ulCount);\r
- return ulLeaksByte;\r
-} // AllocHashOutLeaks\r
-#endif\r
-\r
-\r
-static void AllocHashInit(void) {\r
-\r
- memset(AllocHashTable, 0, sizeof(AllocHashTable));\r
- AllocHashEntries = 0;\r
- AllocHashCollisions = 0;\r
- AllocHashFreed = 0;\r
- AllocHashCurrentCount = 0;\r
- AllocHashMaxUsed = 0;\r
-\r
- AllocHashMaxCollisions = 0;\r
- AllocHashCurrentCollisions = 0;\r
-\r
-#ifdef WITH_IMALLOC_SPY\r
- memset(IMallocHashTable, 0, sizeof(IMallocHashTable));\r
- IMallocHashEntries = 0;\r
- IMallocHashCollisions = 0;\r
- IMallocHashFreed = 0;\r
- IMallocHashCurrentCount = 0;\r
- IMallocHashMaxUsed = 0;\r
-\r
- IMallocHashMaxCollisions = 0;\r
- IMallocHashCurrentCollisions = 0;\r
-#endif\r
- return;\r
-} // AllocHashInit\r
-\r
-\r
-// AllocHashDeinit\r
-// Returns the number of bytes, that are not freed (leaks)\r
-static ULONG AllocHashDeinit(void) {\r
- ULONG ulRet = 0;\r
- bool bAppend = g_CallstackOutputType != ACOutput_XML;\r
- AllocCheckFileOpen(false);//bAppend); // open global log-file\r
-\r
- if (g_CallstackOutputType == ACOutput_XML)\r
- {\r
- _ftprintf(g_fFile, _T("<MEMREPORT "));\r
- WriteDateTime(g_fFile, TRUE);\r
- _ftprintf(g_fFile, _T(">\n"));\r
- }\r
- else\r
- {\r
- _ftprintf(g_fFile, _T("\n##### Memory Report ########################################\n"));\r
- WriteDateTime(g_fFile);\r
- _ftprintf(g_fFile, _T("\n"));\r
- }\r
-\r
-#ifndef HASH_ENTRY_REMOVE_AT_FREE\r
- // output the used memory\r
- if (g_CallstackOutputType != ACOutput_XML)\r
- _ftprintf(g_fFile, _T("##### Memory used: #########################################\n"));\r
- AllocHashOut(g_fFile);\r
-#endif\r
-\r
- // output the Memoty leaks\r
- if (g_CallstackOutputType != ACOutput_XML)\r
- _ftprintf(g_fFile, _T("\n##### Leaks: ###############################################\n"));\r
- ulRet = AllocHashOutLeaks(g_fFile);\r
-\r
- if (g_CallstackOutputType == ACOutput_Advanced)\r
- {\r
- // output some statistics from the hash-table\r
- _ftprintf(g_fFile, _T("***** Hash-Table statistics:\n"));\r
- _ftprintf(g_fFile, _T(" Table-Size: %i\n"), ALLOC_HASH_ENTRIES);\r
- _ftprintf(g_fFile, _T(" Inserts: %i\n"), AllocHashEntries);\r
- _ftprintf(g_fFile, _T(" Freed: %i\n"), AllocHashFreed);\r
- _ftprintf(g_fFile, _T(" Sum Collisions: %i\n"), AllocHashCollisions);\r
- _ftprintf(g_fFile, _T("\n"));\r
- _ftprintf(g_fFile, _T(" Max used: %i\n"), AllocHashMaxUsed);\r
- _ftprintf(g_fFile, _T(" Max Collisions: %i\n"), AllocHashMaxCollisions);\r
- }\r
-\r
- // Free Hash-Table\r
- ULONG ulTemp;\r
- AllocHashEntryType *pHashEntry, *pHashEntryOld;\r
-\r
- // Now, free my own memory\r
- for(ulTemp = 0; ulTemp < ALLOC_HASH_ENTRIES; ulTemp++) {\r
- pHashEntry = &AllocHashTable[ulTemp];\r
- while(pHashEntry != NULL) {\r
- pHashEntryOld = pHashEntry;\r
- pHashEntry = pHashEntry->Next;\r
- if (pHashEntryOld != &AllocHashTable[ulTemp]) {\r
- // now free the dynamically allocated memory\r
- free(pHashEntryOld);\r
- }\r
- } // while\r
- } // for\r
- // empty the hash-table\r
- memset(AllocHashTable, 0, sizeof(AllocHashTable));\r
-\r
-#ifdef WITH_IMALLOC_SPY\r
- // output the Memoty leaks\r
- if (g_CallstackOutputType != ACOutput_XML)\r
- _ftprintf(g_fFile, _T("\n##### COM-Leaks: ###############################################\n"));\r
- ulRet = IMallocHashOutLeaks(g_fFile);\r
-\r
- if (g_CallstackOutputType == ACOutput_Advanced)\r
- {\r
- // output some statistics from the hash-table\r
- _ftprintf(g_fFile, _T("***** Hash-Table statistics:\n"));\r
- _ftprintf(g_fFile, _T(" Table-Size: %i\n"), ALLOC_HASH_ENTRIES);\r
- _ftprintf(g_fFile, _T(" Inserts: %i\n"), IMallocHashEntries);\r
- _ftprintf(g_fFile, _T(" Freed: %i\n"), IMallocHashFreed);\r
- _ftprintf(g_fFile, _T(" Sum Collisions: %i\n"), IMallocHashCollisions);\r
- _ftprintf(g_fFile, _T("\n"));\r
- _ftprintf(g_fFile, _T(" Max used: %i\n"), IMallocHashMaxUsed);\r
- _ftprintf(g_fFile, _T(" Max Collisions: %i\n"), IMallocHashMaxCollisions);\r
- }\r
-\r
- // Free Hash-Table\r
- //ULONG ulTemp;\r
- IMallocHashEntryType *pIMHashEntry, *pIMHashEntryOld;\r
-\r
- // Gehe jeden Eintrag durch und gebe ihn frei\r
- for(ulTemp = 0; ulTemp < ALLOC_HASH_ENTRIES; ulTemp++) {\r
- pIMHashEntry = &IMallocHashTable[ulTemp];\r
- while(pHashEntry != NULL) {\r
- pIMHashEntryOld = pIMHashEntry;\r
- pIMHashEntry = pIMHashEntry->Next;\r
- if (pIMHashEntryOld != &IMallocHashTable[ulTemp]) {\r
- // es ist dynamischer Speicher, gebe ihn also frei:\r
- _free_dbg(pIMHashEntryOld, _CRT_BLOCK);\r
- }\r
- } // while\r
- } // for\r
- // Lösche die gesamte Hash-Tabelle\r
- memset(IMallocHashTable, 0, sizeof(IMallocHashTable));\r
-#endif\r
-\r
-\r
- if (g_CallstackOutputType == ACOutput_XML)\r
- _ftprintf(g_fFile, _T("</MEMREPORT>\n"));\r
-\r
- return ulRet;\r
-} // AllocHashDeinit\r
-\r
-// AllocHashFunction\r
-// The has-function (very simple)\r
-static inline ULONG AllocHashFunction(long lRequestID) {\r
- // I couldn´t find any better and faster\r
- return lRequestID % ALLOC_HASH_ENTRIES;\r
-} // AllocHashFunction\r
-\r
-// AllocHashInsert\r
-// lRequestID: Key-Word (RequestID from AllocHook)\r
-// pContext: Context-Record, for retrieving Callstack (EIP and EBP is only needed)\r
-// nDataSize: How many bytes\r
-static void AllocHashInsert(long lRequestID, CONTEXT &Context, size_t nDataSize) {\r
- ULONG HashIdx;\r
- AllocHashEntryType *pHashEntry;\r
-\r
- // change statistical data\r
- AllocHashEntries++;\r
- AllocHashCurrentCount++;\r
- if (AllocHashCurrentCount > AllocHashMaxUsed)\r
- AllocHashMaxUsed = AllocHashCurrentCount;\r
-\r
- // generate hash-value\r
- HashIdx = AllocHashFunction(lRequestID);\r
-\r
- pHashEntry = &AllocHashTable[HashIdx];\r
- if (pHashEntry->lRequestID == 0) {\r
- // Entry is empty...\r
- }\r
- else {\r
- // Entry is not empy! make a list of entries for this hash value...\r
- // change statistical data\r
- // if this happens often, you should increase the hah size or change the heash-function; \r
- // to fasten the allocation time\r
- AllocHashCollisions++;\r
- AllocHashCurrentCollisions++;\r
- if (AllocHashCurrentCollisions > AllocHashMaxCollisions)\r
- AllocHashMaxCollisions = AllocHashCurrentCollisions;\r
-\r
- while(pHashEntry->Next != NULL) {\r
- pHashEntry = pHashEntry->Next;\r
- }\r
-\r
- pHashEntry->Next = (AllocHashEntryType*) calloc(sizeof(AllocHashEntryType), 1);\r
- pHashEntry = pHashEntry->Next;\r
-\r
- }\r
- pHashEntry->lRequestID = lRequestID; // Key-Word\r
- pHashEntry->nDataSize = nDataSize;\r
- pHashEntry->Next = NULL;\r
- // Get EIP and save it in the record\r
- pHashEntry->dwEIPOffset = Context.Eip;\r
- if (ReadProcessMemory(GetCurrentProcess(), (LPCVOID) Context.Eip, &(pHashEntry->pcEIPAddr), MAX_EIP_LEN_BUF, &(pHashEntry->dwEIPLen)) == 0) {\r
- // Could not read memory... remove everything...\r
- memset(pHashEntry->pcEIPAddr, 0, MAX_EIP_LEN_BUF);\r
- pHashEntry->dwEIPLen = 0;\r
- pHashEntry->dwEIPOffset = 0;\r
- }\r
-\r
- // Get ESP and save it in the record\r
- pHashEntry->dwESPOffset = Context.Ebp;\r
- if (ReadProcessMemory(GetCurrentProcess(), (LPCVOID) Context.Ebp, &(pHashEntry->pcESPAddr), MAX_ESP_LEN_BUF, &(pHashEntry->dwESPLen)) == 0) {\r
- // Could not read memory... remove everything...\r
- memset(pHashEntry->pcESPAddr, 0, MAX_ESP_LEN_BUF);\r
- pHashEntry->dwESPLen = 0;\r
- pHashEntry->dwESPOffset = 0;\r
-\r
- // Check if I tried to read too much...\r
- if (GetLastError() == ERROR_PARTIAL_COPY)\r
- {\r
- // ask how many I can read:\r
- MEMORY_BASIC_INFORMATION MemBuffer;\r
- DWORD dwRet = VirtualQuery((LPCVOID) Context.Ebp, &MemBuffer, sizeof(MemBuffer));\r
- if (dwRet > 0)\r
- {\r
- // calculate the length\r
- DWORD len = ((DWORD) MemBuffer.BaseAddress + MemBuffer.RegionSize) - Context.Ebp;\r
- if ( (len > 0) && (len < MAX_ESP_LEN_BUF) )\r
- {\r
- // try to read it again (with the shorter length)\r
- if (ReadProcessMemory(GetCurrentProcess(), (LPCVOID) Context.Ebp, &(pHashEntry->pcESPAddr), len, &(pHashEntry->dwESPLen)) == 0)\r
- {\r
- // ok, now everything goes wrong... remove it...\r
- memset(pHashEntry->pcESPAddr, 0, MAX_ESP_LEN_BUF);\r
- pHashEntry->dwESPLen = 0;\r
- pHashEntry->dwESPOffset = 0;\r
- }\r
- else\r
- {\r
- pHashEntry->dwESPOffset = Context.Ebp;\r
- }\r
- }\r
- } // VirtualQuery was successfully\r
- } // ERROR_PARTIAL_COPY\r
- }\r
-}\r
-\r
-// AllocHashFind\r
-// If ALLOC_ENTRY_NOT_FOUND is returned, the Key was not found!\r
-// If the Key was found, a pointer to the entry is returned\r
-static AllocHashEntryType *AllocHashFind(long lRequestID) {\r
- ULONG HashIdx;\r
- AllocHashEntryType *pHashEntry;\r
-\r
- // get the Hash-Value\r
- HashIdx = AllocHashFunction(lRequestID);\r
-\r
- // Just do some simple checks:\r
- _ASSERTE(HashIdx < ALLOC_HASH_ENTRIES);\r
-\r
- pHashEntry = &AllocHashTable[HashIdx];\r
- while(pHashEntry != NULL) {\r
- if (pHashEntry->lRequestID == lRequestID) {\r
- return pHashEntry;\r
- }\r
- pHashEntry = pHashEntry->Next;\r
- }\r
-\r
- // entry was not found!\r
- return (AllocHashEntryType*) ALLOC_ENTRY_NOT_FOUND;\r
-} // AllocHashFind\r
-\r
-// AllocHashRemove\r
-// Return: FALSE (0) : Key was found and removed/marked\r
-// TRUE (!=0): Key was not found\r
-static BOOL AllocHashRemove(long lRequestID) {\r
- ULONG HashIdx;\r
- AllocHashEntryType *pHashEntry, *pHashEntryLast;\r
-\r
- // get the Hash-Value\r
- HashIdx = AllocHashFunction(lRequestID);\r
-\r
- // Just do some simple checks:\r
- _ASSERTE(HashIdx < ALLOC_HASH_ENTRIES);\r
-\r
- pHashEntryLast = NULL;\r
- pHashEntry = &AllocHashTable[HashIdx];\r
- while(pHashEntry != NULL) {\r
- if (pHashEntry->lRequestID == lRequestID) {\r
-#ifdef HASH_ENTRY_REMOVE_AT_FREE\r
- AllocHashFreed++;\r
- AllocHashCurrentCount--;\r
- // release my memory\r
- if (pHashEntryLast == NULL) {\r
- // It is an entry in the table, so do not release this memory\r
- if (pHashEntry->Next == NULL) {\r
- // It was the last entry, so empty the table entry\r
- memset(&AllocHashTable[HashIdx], 0, sizeof(AllocHashTable[HashIdx]));\r
- }\r
- else {\r
- // There are some more entries, so shorten the list\r
- AllocHashEntryType *pTmp = pHashEntry->Next;\r
- *pHashEntry = *(pHashEntry->Next);\r
- free(pTmp);\r
- }\r
- return TRUE;\r
- }\r
- else {\r
- // now, I am in an dynamic allocated entry\r
- // it was a collision, so decrease the current collision count\r
- AllocHashCurrentCollisions--;\r
- pHashEntryLast->Next = pHashEntry->Next;\r
- free(pHashEntry);\r
- return TRUE;\r
- }\r
-#else\r
- // increase the Remove-Count and let the objet stay in memory\r
- pHashEntry->cRemovedFlag++;\r
- return TRUE;\r
-#endif\r
- }\r
- pHashEntryLast = pHashEntry;\r
- pHashEntry = pHashEntry->Next;\r
- }\r
-\r
- // if we are here, we could not find the RequestID\r
- return FALSE;\r
-}\r
-\r
-// ReadProcMemoryFromHash\r
-// Callback-Funtion for StackWalk for my own CallStack from the Hash-Table-Entries\r
-static BOOL __stdcall ReadProcMemoryFromHash(HANDLE hRequestID, DWORD64 lpBaseAddress, PVOID lpBuffer, DWORD nSize, PDWORD lpNumberOfBytesRead) {\r
- // Try to find the RequestID\r
- AllocHashEntryType *pHashEntry;\r
- *lpNumberOfBytesRead = 0;\r
-\r
- pHashEntry = AllocHashFind((LONG) hRequestID);\r
- if (pHashEntry == (AllocHashEntryType*) ALLOC_ENTRY_NOT_FOUND) {\r
- // Not found, so I cannot return any memory\r
- *lpNumberOfBytesRead = 0;\r
- return FALSE;\r
- }\r
- if ( ((DWORD) lpBaseAddress >= pHashEntry->dwESPOffset) && ((DWORD) lpBaseAddress <= (pHashEntry->dwESPOffset+pHashEntry->dwESPLen)) ) {\r
- // Memory is located in ESP:\r
- // Calculate the offset\r
- DWORD dwOffset = (DWORD) lpBaseAddress - pHashEntry->dwESPOffset;\r
- DWORD dwSize = __min(nSize, MAX_ESP_LEN_BUF-dwOffset);\r
- memcpy(lpBuffer, &(pHashEntry->pcESPAddr[dwOffset]), dwSize);\r
- *lpNumberOfBytesRead = dwSize;\r
- if (dwSize != nSize)\r
- return FALSE;\r
- }\r
-\r
- if ( ((DWORD) lpBaseAddress >= pHashEntry->dwEIPOffset) && ((DWORD) lpBaseAddress <= (pHashEntry->dwEIPOffset+pHashEntry->dwEIPLen)) ) {\r
- // Memory is located in EIP:\r
- // Calculate the offset\r
- DWORD dwOffset = (DWORD) lpBaseAddress - pHashEntry->dwEIPOffset;\r
- DWORD dwSize = __min(nSize, MAX_ESP_LEN_BUF-dwOffset);\r
- memcpy(lpBuffer, &(pHashEntry->pcEIPAddr[dwOffset]), dwSize);\r
- *lpNumberOfBytesRead = dwSize;\r
- if (dwSize != nSize)\r
- return FALSE;\r
- }\r
- \r
- if (*lpNumberOfBytesRead == 0) // Memory could not be found\r
- return FALSE;\r
-\r
- return TRUE;\r
-}\r
-\r
-// AllocHashOutLeaks\r
-// Write all Memory (with callstack) which was not freed yet\r
-// Returns the number of bytes, that are not freed (leaks)\r
-ULONG AllocHashOutLeaks(FILE *fFile) {\r
- ULONG ulTemp;\r
- AllocHashEntryType *pHashEntry;\r
- ULONG ulCount = 0;\r
- ULONG ulLeaksByte = 0;\r
-\r
- // Move throu every entry\r
- for(ulTemp = 0; ulTemp < ALLOC_HASH_ENTRIES; ulTemp++) {\r
- pHashEntry = &AllocHashTable[ulTemp];\r
- if (pHashEntry->lRequestID != 0) {\r
- while(pHashEntry != NULL) {\r
- if ( (pHashEntry->cRemovedFlag <= 0) || (pHashEntry->cRemovedFlag > 1) ) {\r
- ulCount++;\r
- if (g_CallstackOutputType == ACOutput_XML)\r
- _ftprintf(fFile, _T("<LEAK requestID=\"%u\" size=\"%u\">\n"), pHashEntry->lRequestID, pHashEntry->nDataSize);\r
- else\r
- _ftprintf(fFile, _T("RequestID: %12i, Removed: %i, Size: %12i\n"), pHashEntry->lRequestID, pHashEntry->cRemovedFlag, pHashEntry->nDataSize);\r
- CONTEXT c;\r
- memset( &c, '\0', sizeof c );\r
- c.Eip = pHashEntry->dwEIPOffset;\r
- c.Ebp = pHashEntry->dwESPOffset;\r
- ShowStackRM( NULL, c, fFile, &ReadProcMemoryFromHash, (HANDLE) pHashEntry->lRequestID);\r
- // Count the number of leaky bytes\r
- if (pHashEntry->nDataSize > 0)\r
- ulLeaksByte += pHashEntry->nDataSize;\r
- else\r
- ulLeaksByte++; // If memory was allocated with zero bytes, then just increase the counter 1\r
-\r
- if (g_CallstackOutputType == ACOutput_XML)\r
- _ftprintf(fFile, _T("</LEAK>\n")); // terminate the xml-node\r
- }\r
- pHashEntry = pHashEntry->Next;\r
- }\r
- }\r
- }\r
- if (g_CallstackOutputType != ACOutput_XML)\r
- _ftprintf(fFile, _T("\n**** Number of leaks: %i\n"), ulCount);\r
- return ulLeaksByte;\r
-} // AllocHashOutLeaks\r
-\r
-// Write all used memory to a file\r
-void AllocHashOut(FILE *fFile) {\r
- ULONG ulTemp;\r
- AllocHashEntryType *pHashEntry;\r
-\r
- for(ulTemp = 0; ulTemp < ALLOC_HASH_ENTRIES; ulTemp++) {\r
- pHashEntry = &AllocHashTable[ulTemp];\r
- if (pHashEntry->lRequestID != 0) {\r
- while(pHashEntry != NULL) {\r
- if (g_CallstackOutputType == ACOutput_XML)\r
- _ftprintf(fFile, _T("<MEMUSED requestID=\"%u\" size=\"%u\"\n"), pHashEntry->lRequestID, pHashEntry->nDataSize);\r
- else\r
- _ftprintf(fFile, _T("RequestID: %12i, Removed: %i, Size: %12i\n"), pHashEntry->lRequestID, pHashEntry->cRemovedFlag, pHashEntry->nDataSize);\r
- pHashEntry = pHashEntry->Next;\r
- }\r
- }\r
- }\r
-} // AllocHashOut\r
-\r
-/*******************************************************************************\r
- * Ende der Hash-Tabelle\r
- *******************************************************************************/\r
-\r
-\r
-// The follwoing is copied from dbgint.h:\r
-// <CRT_INTERNALS>\r
-/*\r
- * For diagnostic purpose, blocks are allocated with extra information and\r
- * stored in a doubly-linked list. This makes all blocks registered with\r
- * how big they are, when they were allocated, and what they are used for.\r
- */\r
-\r
-#define nNoMansLandSize 4\r
-\r
-typedef struct _CrtMemBlockHeader\r
-{\r
- struct _CrtMemBlockHeader * pBlockHeaderNext;\r
- struct _CrtMemBlockHeader * pBlockHeaderPrev;\r
- char * szFileName;\r
- int nLine;\r
-#ifdef _WIN64\r
- /* These items are reversed on Win64 to eliminate gaps in the struct\r
- * and ensure that sizeof(struct)%16 == 0, so 16-byte alignment is\r
- * maintained in the debug heap.\r
- */\r
- int nBlockUse;\r
- size_t nDataSize;\r
-#else /* _WIN64 */\r
- size_t nDataSize;\r
- int nBlockUse;\r
-#endif /* _WIN64 */\r
- long lRequest;\r
- unsigned char gap[nNoMansLandSize];\r
- /* followed by:\r
- * unsigned char data[nDataSize];\r
- * unsigned char anotherGap[nNoMansLandSize];\r
- */\r
-} _CrtMemBlockHeader;\r
-#define pbData(pblock) ((unsigned char *)((_CrtMemBlockHeader *)pblock + 1))\r
-#define pHdr(pbData) (((_CrtMemBlockHeader *)pbData)-1)\r
- \r
-// </CRT_INTERNALS>\r
-\r
-\r
-\r
-\r
-// Global data:\r
-static BOOL g_bInitialized = FALSE;\r
-static HINSTANCE g_hImagehlpDll = NULL;\r
-\r
-static DWORD g_dwShowCount = 0; // increase at every ShowStack-Call\r
-static CRITICAL_SECTION g_csFileOpenClose = {0};\r
-\r
-// Is used for syncronising call to MyAllocHook (to prevent reentrant calls)\r
-static LONG g_lMallocCalled = 0;\r
-\r
-static _CRT_ALLOC_HOOK pfnOldCrtAllocHook = NULL;\r
-\r
-// Deaktivate AllocHook, by increasing the Syncronisation-Counter\r
-//static void DeactivateMallocStackwalker(void) {\r
-// InterlockedIncrement(&g_lMallocCalled);\r
-//}\r
-\r
-\r
-// MyAllocHook is Single-Threaded, that means the the calls are serialized in the calling function!\r
-// Special case for VC 5\r
-#if _MSC_VER <= 1100 \r
-static int MyAllocHook(int nAllocType, void *pvData, \r
- size_t nSize, int nBlockUse, long lRequest, \r
- const char * szFileName, int nLine ) {\r
-#else\r
-static int MyAllocHook(int nAllocType, void *pvData, \r
- size_t nSize, int nBlockUse, long lRequest, \r
- const unsigned char * szFileName, int nLine ) {\r
-#endif\r
- static TCHAR *operation[] = { _T(""), _T("ALLOCATIONG"), _T("RE-ALLOCATING"), _T("FREEING") };\r
- static TCHAR *blockType[] = { _T("Free"), _T("Normal"), _T("CRT"), _T("Ignore"), _T("Client") };\r
-\r
-#ifdef IGNORE_CRT_ALLOC\r
- if (_BLOCK_TYPE(nBlockUse) == _CRT_BLOCK) // Ignore internal C runtime library allocations\r
- return TRUE;\r
-#endif\r
- extern int _crtDbgFlag;\r
- if ( ((_CRTDBG_ALLOC_MEM_DF & _crtDbgFlag) == 0) && ( (nAllocType == _HOOK_ALLOC) || (nAllocType == _HOOK_REALLOC) ) )\r
- {\r
- // Someone has disabled that the runtime should log this allocation\r
- // so we do not log this allocation\r
- if (pfnOldCrtAllocHook != NULL)\r
- pfnOldCrtAllocHook(nAllocType, pvData, nSize, nBlockUse, lRequest, szFileName, nLine);\r
- return TRUE;\r
- }\r
-\r
- // Prevent from reentrat calls\r
- if (InterlockedIncrement(&g_lMallocCalled) > 1) { // I was already called\r
- InterlockedDecrement(&g_lMallocCalled);\r
- // call the previous alloc hook\r
- if (pfnOldCrtAllocHook != NULL)\r
- pfnOldCrtAllocHook(nAllocType, pvData, nSize, nBlockUse, lRequest, szFileName, nLine);\r
- return TRUE;\r
- }\r
-\r
- if (g_ulShowStackAtAlloc > 0) {\r
- AllocCheckFileOpen(); // Open logfile\r
- }\r
-\r
- _ASSERT( (nAllocType == _HOOK_ALLOC) || (nAllocType == _HOOK_REALLOC) || (nAllocType == _HOOK_FREE) );\r
- _ASSERT( ( _BLOCK_TYPE(nBlockUse) >= 0 ) && ( _BLOCK_TYPE(nBlockUse) < 5 ) );\r
-\r
- if (nAllocType == _HOOK_FREE) { // freeing\r
- // Try to get the header information\r
- if (_CrtIsValidHeapPointer(pvData)) { // it is a valid Heap-Pointer\r
- // get the ID\r
- _CrtMemBlockHeader *pHead;\r
- // get a pointer to memory block header\r
- pHead = pHdr(pvData);\r
- nSize = pHead->nDataSize;\r
- lRequest = pHead->lRequest; // This is the ID!\r
-\r
- if (pHead->nBlockUse == _IGNORE_BLOCK)\r
- {\r
- InterlockedDecrement(&g_lMallocCalled);\r
- if (pfnOldCrtAllocHook != NULL)\r
- pfnOldCrtAllocHook(nAllocType, pvData, nSize, nBlockUse, lRequest, szFileName, nLine);\r
- return TRUE;\r
- }\r
- }\r
- }\r
-\r
- if (g_ulShowStackAtAlloc > 0) {\r
- _ftprintf( g_fFile, _T("##### Memory operation: %s a %d-byte '%s' block (# %ld)"),\r
- operation[nAllocType], nSize, blockType[_BLOCK_TYPE(nBlockUse)], lRequest );\r
- if ( pvData != NULL )\r
- _ftprintf( g_fFile, _T(" at 0x%X"), pvData );\r
- _ftprintf(g_fFile, _T("\n"));\r
- }\r
-\r
- if (nAllocType == _HOOK_FREE) { // freeing:\r
- if (lRequest != 0) { // RequestID was found\r
- BOOL bRet;\r
- // Try to find the RequestID in the Hash-Table, mark it that it was freed\r
- bRet = AllocHashRemove(lRequest);\r
- if(g_ulShowStackAtAlloc > 0) {\r
- if (bRet == FALSE) {\r
- // RequestID not found!\r
- _ftprintf(g_fFile, _T("###### RequestID not found in hash table for FREEING (%i)!\n"), lRequest);\r
- }\r
- } // g_ulShowStackAtAlloc > 0\r
- }\r
- else {\r
- if(g_ulShowStackAtAlloc > 0) {\r
- // No valid RequestID found, display error\r
- _ftprintf(g_fFile, _T("###### No valid RequestID for FREEING! (0x%X)\n"), pvData);\r
- \r
- }\r
- }\r
- } // freeing\r
-\r
- if (nAllocType == _HOOK_REALLOC) { // re-allocating\r
- // Try to get the header information\r
- if (_CrtIsValidHeapPointer(pvData)) { // it is a valid Heap-Pointer\r
- BOOL bRet;\r
- LONG lReallocRequest;\r
- // get the ID\r
- _CrtMemBlockHeader *pHead;\r
- // get a pointer to memory block header\r
- pHead = pHdr(pvData);\r
- // Try to find the RequestID in the Hash-Table, mark it that it was freed\r
- lReallocRequest = pHead->lRequest;\r
- bRet = AllocHashRemove(lReallocRequest);\r
- if (g_ulShowStackAtAlloc > 0) {\r
- if (bRet == FALSE) {\r
- // RequestID not found!\r
- _ftprintf(g_fFile, _T("###### RequestID not found in hash table for RE-ALLOCATING (%i)!\n"), lReallocRequest);\r
- }\r
- else {\r
- _ftprintf(g_fFile, _T("##### Implicit freeing because of re-allocation (# old: %ld, new: %ld)\n"), lReallocRequest, lRequest);\r
- }\r
- } // g_ulShowStackAtAlloc > 0\r
- } // ValidHeapPointer\r
- } // re-allocating\r
-\r
- if ( (g_ulShowStackAtAlloc < 3) && (nAllocType == _HOOK_FREE) ) {\r
- InterlockedDecrement(&g_lMallocCalled);\r
- // call the previous alloc hook\r
- if (pfnOldCrtAllocHook != NULL)\r
- pfnOldCrtAllocHook(nAllocType, pvData, nSize, nBlockUse, lRequest, szFileName, nLine);\r
- return TRUE;\r
- }\r
-\r
- HANDLE hThread;\r
- if (DuplicateHandle( GetCurrentProcess(), GetCurrentThread(),\r
- GetCurrentProcess(), &hThread, 0, false, DUPLICATE_SAME_ACCESS ) == 0) {\r
- // Something was wrong...\r
- _ftprintf(g_fFile, _T("###### Could not call 'DuplicateHandle' successfully\n"));\r
- InterlockedDecrement(&g_lMallocCalled);\r
- // call the previous alloc hook\r
- if (pfnOldCrtAllocHook != NULL)\r
- pfnOldCrtAllocHook(nAllocType, pvData, nSize, nBlockUse, lRequest, szFileName, nLine);\r
- return TRUE;\r
- }\r
-\r
- CONTEXT c;\r
- memset( &c, '\0', sizeof c );\r
- c.ContextFlags = CONTEXT_FULL;\r
-\r
- // Get the context of this thread\r
-#if 0\r
- // init CONTEXT record so we know where to start the stackwalk\r
- if ( MyGetCurrentThreadContext( hThread, &c ) == 0) {\r
- if(g_ulShowStackAtAlloc > 1) {\r
- _ftprintf(g_fFile, _T("###### Could not call 'GetThreadContext' successfully\n"));\r
- }\r
- InterlockedDecrement(&g_lMallocCalled);\r
- // call the previous alloc hook\r
- if (pfnOldCrtAllocHook != NULL)\r
- pfnOldCrtAllocHook(nAllocType, pvData, nSize, nBlockUse, lRequest, szFileName, nLine);\r
- CloseHandle(hThread);\r
- return TRUE; // could not get context\r
- }\r
-#else\r
- __asm\r
- {\r
- call x\r
- x: pop eax\r
- mov c.Eip, eax\r
- mov c.Ebp, ebp\r
- }\r
-#endif\r
-\r
- if(g_ulShowStackAtAlloc > 1) {\r
- if(g_ulShowStackAtAlloc > 2) {\r
- // output the callstack\r
- ShowStack( hThread, c, g_fFile);\r
- }\r
- else {\r
- // Output only (re)allocs\r
- if (nAllocType != _HOOK_FREE) {\r
- ShowStack( hThread, c, g_fFile);\r
- }\r
- }\r
- } // g_ulShowStackAtAlloc > 1\r
- CloseHandle( hThread );\r
-\r
- // Only isert in the Hash-Table if it is not a "freeing"\r
- if (nAllocType != _HOOK_FREE) {\r
- if(lRequest != 0) // Always a valid RequestID should be provided (see comments in the header)\r
- AllocHashInsert(lRequest, c, nSize);\r
- }\r
-\r
- InterlockedDecrement(&g_lMallocCalled);\r
- // call the previous alloc hook\r
- if (pfnOldCrtAllocHook != NULL)\r
- pfnOldCrtAllocHook(nAllocType, pvData, nSize, nBlockUse, lRequest, szFileName, nLine);\r
- return TRUE; // allow the memory operation to proceed\r
-}\r
-\r
-\r
-\r
-\r
-// ##########################################################################################\r
-// ##########################################################################################\r
-// ##########################################################################################\r
-// ##########################################################################################\r
-\r
-#define gle (GetLastError())\r
-#define lenof(a) (sizeof(a) / sizeof((a)[0]))\r
-#define MAXNAMELEN 1024 // max name length for found symbols\r
-#define IMGSYMLEN ( sizeof IMAGEHLP_SYMBOL64 )\r
-#define TTBUFLEN 8096 // for a temp buffer (2^13)\r
-\r
-\r
-\r
-// SymCleanup()\r
-typedef BOOL (__stdcall *tSC)( IN HANDLE hProcess );\r
-tSC pSC = NULL;\r
-\r
-// SymFunctionTableAccess64()\r
-typedef PVOID (__stdcall *tSFTA)( HANDLE hProcess, DWORD64 AddrBase );\r
-tSFTA pSFTA = NULL;\r
-\r
-// SymGetLineFromAddr64()\r
-typedef BOOL (__stdcall *tSGLFA)( IN HANDLE hProcess, IN DWORD64 dwAddr,\r
- OUT PDWORD pdwDisplacement, OUT PIMAGEHLP_LINE64 Line );\r
-tSGLFA pSGLFA = NULL;\r
-\r
-// SymGetModuleBase64()\r
-typedef DWORD64 (__stdcall *tSGMB)( IN HANDLE hProcess, IN DWORD64 dwAddr );\r
-tSGMB pSGMB = NULL;\r
-\r
-// SymGetModuleInfo64()\r
-typedef BOOL (__stdcall *tSGMI)( IN HANDLE hProcess, IN DWORD64 dwAddr, OUT PIMAGEHLP_MODULE64 ModuleInfo );\r
-tSGMI pSGMI = NULL;\r
-\r
-// SymGetOptions()\r
-typedef DWORD (__stdcall *tSGO)( VOID );\r
-tSGO pSGO = NULL;\r
-\r
-// SymGetSymFromAddr64()\r
-typedef BOOL (__stdcall *tSGSFA)( IN HANDLE hProcess, IN DWORD64 dwAddr,\r
- OUT PDWORD64 pdwDisplacement, OUT PIMAGEHLP_SYMBOL64 Symbol );\r
-tSGSFA pSGSFA = NULL;\r
-\r
-// SymInitialize()\r
-typedef BOOL (__stdcall *tSI)( IN HANDLE hProcess, IN PSTR UserSearchPath, IN BOOL fInvadeProcess );\r
-tSI pSI = NULL;\r
-\r
-// SymLoadModule64()\r
-typedef DWORD (__stdcall *tSLM)( IN HANDLE hProcess, IN HANDLE hFile,\r
- IN PSTR ImageName, IN PSTR ModuleName, IN DWORD64 BaseOfDll, IN DWORD SizeOfDll );\r
-tSLM pSLM = NULL;\r
-\r
-// SymSetOptions()\r
-typedef DWORD (__stdcall *tSSO)( IN DWORD SymOptions );\r
-tSSO pSSO = NULL;\r
-\r
-// StackWalk64()\r
-typedef BOOL (__stdcall *tSW)( \r
- DWORD MachineType, \r
- HANDLE hProcess,\r
- HANDLE hThread, \r
- LPSTACKFRAME64 StackFrame, \r
- PVOID ContextRecord,\r
- PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryRoutine,\r
- PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine,\r
- PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine,\r
- PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress );\r
-tSW pSW = NULL;\r
-\r
-// UnDecorateSymbolName()\r
-typedef DWORD (__stdcall WINAPI *tUDSN)( PCSTR DecoratedName, PSTR UnDecoratedName,\r
- DWORD UndecoratedLength, DWORD Flags );\r
-tUDSN pUDSN = NULL;\r
-\r
-\r
-\r
-struct ModuleEntry\r
-{\r
- std::string imageName;\r
- std::string moduleName;\r
- DWORD baseAddress;\r
- DWORD size;\r
-};\r
-typedef std::vector< ModuleEntry > ModuleList;\r
-typedef ModuleList::iterator ModuleListIter;\r
-\r
-// **************************************** ToolHelp32 ************************\r
-#define MAX_MODULE_NAME32 255\r
-#define TH32CS_SNAPMODULE 0x00000008\r
-#pragma pack( push, 8 )\r
-typedef struct tagMODULEENTRY32\r
-{\r
- DWORD dwSize;\r
- DWORD th32ModuleID; // This module\r
- DWORD th32ProcessID; // owning process\r
- DWORD GlblcntUsage; // Global usage count on the module\r
- DWORD ProccntUsage; // Module usage count in th32ProcessID's context\r
- BYTE * modBaseAddr; // Base address of module in th32ProcessID's context\r
- DWORD modBaseSize; // Size in bytes of module starting at modBaseAddr\r
- HMODULE hModule; // The hModule of this module in th32ProcessID's context\r
- char szModule[MAX_MODULE_NAME32 + 1];\r
- char szExePath[MAX_PATH];\r
-} MODULEENTRY32;\r
-typedef MODULEENTRY32 * PMODULEENTRY32;\r
-typedef MODULEENTRY32 * LPMODULEENTRY32;\r
-#pragma pack( pop )\r
-\r
-\r
-\r
-static bool GetModuleListTH32(ModuleList& modules, DWORD pid, FILE *fLogFile)\r
-{\r
- // CreateToolhelp32Snapshot()\r
- typedef HANDLE (__stdcall *tCT32S)(DWORD dwFlags, DWORD th32ProcessID);\r
- // Module32First()\r
- typedef BOOL (__stdcall *tM32F)(HANDLE hSnapshot, LPMODULEENTRY32 lpme);\r
- // Module32Next()\r
- typedef BOOL (__stdcall *tM32N)(HANDLE hSnapshot, LPMODULEENTRY32 lpme);\r
-\r
- // try both dlls...\r
- const TCHAR *dllname[] = { _T("kernel32.dll"), _T("tlhelp32.dll") };\r
- HINSTANCE hToolhelp;\r
- tCT32S pCT32S;\r
- tM32F pM32F;\r
- tM32N pM32N;\r
-\r
- HANDLE hSnap;\r
- MODULEENTRY32 me;\r
- me.dwSize = sizeof(me);\r
- bool keepGoing;\r
- ModuleEntry e;\r
- int i;\r
-\r
- for (i = 0; i<lenof(dllname); i++ )\r
- {\r
- hToolhelp = LoadLibrary( dllname[i] );\r
- if (hToolhelp == NULL)\r
- continue;\r
- pCT32S = (tCT32S) GetProcAddress(hToolhelp, "CreateToolhelp32Snapshot");\r
- pM32F = (tM32F) GetProcAddress(hToolhelp, "Module32First");\r
- pM32N = (tM32N) GetProcAddress(hToolhelp, "Module32Next");\r
- if ( pCT32S != 0 && pM32F != 0 && pM32N != 0 )\r
- break; // found the functions!\r
- FreeLibrary(hToolhelp);\r
- hToolhelp = NULL;\r
- }\r
-\r
- if (hToolhelp == NULL)\r
- return false;\r
-\r
- hSnap = pCT32S( TH32CS_SNAPMODULE, pid );\r
- if (hSnap == (HANDLE) -1)\r
- return false;\r
-\r
- keepGoing = !!pM32F( hSnap, &me );\r
- while (keepGoing)\r
- {\r
- e.imageName = me.szExePath;\r
- e.moduleName = me.szModule;\r
- e.baseAddress = (DWORD) me.modBaseAddr;\r
- e.size = me.modBaseSize;\r
- modules.push_back( e );\r
- keepGoing = !!pM32N( hSnap, &me );\r
- }\r
-\r
- CloseHandle(hSnap);\r
- FreeLibrary(hToolhelp);\r
-\r
- return modules.size() != 0;\r
-} // GetModuleListTH32\r
-\r
-\r
-// **************************************** PSAPI ************************\r
-typedef struct _MODULEINFO {\r
- LPVOID lpBaseOfDll;\r
- DWORD SizeOfImage;\r
- LPVOID EntryPoint;\r
-} MODULEINFO, *LPMODULEINFO;\r
-\r
-static bool GetModuleListPSAPI(ModuleList &modules, DWORD pid, HANDLE hProcess, FILE *fLogFile)\r
-{\r
- // EnumProcessModules()\r
- typedef BOOL (__stdcall *tEPM)(HANDLE hProcess, HMODULE *lphModule, DWORD cb, LPDWORD lpcbNeeded );\r
- // GetModuleFileNameEx()\r
- typedef DWORD (__stdcall *tGMFNE)(HANDLE hProcess, HMODULE hModule, LPSTR lpFilename, DWORD nSize );\r
- // GetModuleBaseName()\r
- typedef DWORD (__stdcall *tGMBN)(HANDLE hProcess, HMODULE hModule, LPSTR lpFilename, DWORD nSize );\r
- // GetModuleInformation()\r
- typedef BOOL (__stdcall *tGMI)(HANDLE hProcess, HMODULE hModule, LPMODULEINFO pmi, DWORD nSize );\r
-\r
- HINSTANCE hPsapi;\r
- tEPM pEPM;\r
- tGMFNE pGMFNE;\r
- tGMBN pGMBN;\r
- tGMI pGMI;\r
-\r
- DWORD i;\r
- ModuleEntry e;\r
- DWORD cbNeeded;\r
- MODULEINFO mi;\r
- HMODULE *hMods = 0;\r
- char *tt = 0;\r
-\r
- hPsapi = LoadLibrary( _T("psapi.dll") );\r
- if ( hPsapi == 0 )\r
- return false;\r
-\r
- modules.clear();\r
-\r
- pEPM = (tEPM) GetProcAddress( hPsapi, "EnumProcessModules" );\r
- pGMFNE = (tGMFNE) GetProcAddress( hPsapi, "GetModuleFileNameExA" );\r
- pGMBN = (tGMFNE) GetProcAddress( hPsapi, "GetModuleBaseNameA" );\r
- pGMI = (tGMI) GetProcAddress( hPsapi, "GetModuleInformation" );\r
- if ( pEPM == 0 || pGMFNE == 0 || pGMBN == 0 || pGMI == 0 )\r
- {\r
- // we couldn´t find all functions\r
- FreeLibrary( hPsapi );\r
- return false;\r
- }\r
-\r
- hMods = (HMODULE*) malloc(sizeof(HMODULE) * (TTBUFLEN / sizeof HMODULE));\r
- tt = (char*) malloc(sizeof(char) * TTBUFLEN);\r
-\r
- if ( ! pEPM( hProcess, hMods, TTBUFLEN, &cbNeeded ) )\r
- {\r
- _ftprintf(fLogFile, _T("%lu: EPM failed, GetLastError = %lu\n"), g_dwShowCount, gle );\r
- goto cleanup;\r
- }\r
-\r
- if ( cbNeeded > TTBUFLEN )\r
- {\r
- _ftprintf(fLogFile, _T("%lu: More than %lu module handles. Huh?\n"), g_dwShowCount, lenof( hMods ) );\r
- goto cleanup;\r
- }\r
-\r
- for ( i = 0; i < cbNeeded / sizeof hMods[0]; i++ )\r
- {\r
- // base address, size\r
- pGMI(hProcess, hMods[i], &mi, sizeof mi );\r
- e.baseAddress = (DWORD) mi.lpBaseOfDll;\r
- e.size = mi.SizeOfImage;\r
- // image file name\r
- tt[0] = 0;\r
- pGMFNE(hProcess, hMods[i], tt, TTBUFLEN );\r
- e.imageName = tt;\r
- // module name\r
- tt[0] = 0;\r
- pGMBN(hProcess, hMods[i], tt, TTBUFLEN );\r
- e.moduleName = tt;\r
-\r
- modules.push_back(e);\r
- }\r
-\r
-cleanup:\r
- if (hPsapi)\r
- FreeLibrary(hPsapi);\r
- free(tt);\r
- free(hMods);\r
-\r
- return modules.size() != 0;\r
-} // GetModuleListPSAPI\r
-\r
-\r
-static bool GetModuleList(ModuleList& modules, DWORD pid, HANDLE hProcess, FILE *fLogFile)\r
-{\r
- // first try toolhelp32\r
- if (GetModuleListTH32(modules, pid, fLogFile) )\r
- return true;\r
- // then try psapi\r
- return GetModuleListPSAPI(modules, pid, hProcess, fLogFile);\r
-} // GetModuleList\r
-\r
-\r
-static void EnumAndLoadModuleSymbols( HANDLE hProcess, DWORD pid, FILE *fLogFile )\r
-{\r
- static ModuleList modules;\r
- static ModuleListIter it;\r
- char *img, *mod;\r
-\r
- // fill in module list\r
- GetModuleList(modules, pid, hProcess, fLogFile);\r
-\r
- for ( it = modules.begin(); it != modules.end(); ++ it )\r
- {\r
- // SymLoadModule() wants writeable strings\r
- img = strdup(it->imageName.c_str());\r
- mod = strdup(it->moduleName.c_str());\r
-\r
- pSLM( hProcess, 0, img, mod, it->baseAddress, it->size );\r
-\r
- free(img);\r
- free(mod);\r
- std::string s;\r
- }\r
-} // EnumAndLoadModuleSymbols\r
-\r
-static int InitStackWalk(void)\r
-{\r
- if (g_bInitialized != FALSE)\r
- return 0; // already initialized\r
-\r
- // 02-12-19: Now we only support dbghelp.dll!\r
- // To use it on NT you have to install the redistrubutable for DBGHELP.DLL\r
- g_hImagehlpDll = LoadLibrary( _T("dbghelp.dll") );\r
- if ( g_hImagehlpDll == NULL )\r
- {\r
- printf( "LoadLibrary( \"dbghelp.dll\" ): GetLastError = %lu\n", gle );\r
- g_bInitialized = FALSE;\r
- return 1;\r
- }\r
-\r
- // now we only support the newer dbghlp.dll with the "64"-functions (StackWalk64, a.s.o.)\r
- // If your dbghlp.dll does not support this, please download the redistributable from MS\r
- // Normally from: http://www.microsoft.com/downloads/details.aspx?displaylang=en&FamilyID=CD1FC4B2-0885-47F4-AF45-7FD5E14DB6C0\r
-\r
- pSC = (tSC) GetProcAddress( g_hImagehlpDll, "SymCleanup" );\r
- pSFTA = (tSFTA) GetProcAddress( g_hImagehlpDll, "SymFunctionTableAccess64" );\r
- pSGLFA = (tSGLFA) GetProcAddress( g_hImagehlpDll, "SymGetLineFromAddr64" );\r
- pSGMB = (tSGMB) GetProcAddress( g_hImagehlpDll, "SymGetModuleBase64" );\r
- pSGMI = (tSGMI) GetProcAddress( g_hImagehlpDll, "SymGetModuleInfo64" );\r
- pSGO = (tSGO) GetProcAddress( g_hImagehlpDll, "SymGetOptions" );\r
- pSGSFA = (tSGSFA) GetProcAddress( g_hImagehlpDll, "SymGetSymFromAddr64" );\r
- pSI = (tSI) GetProcAddress( g_hImagehlpDll, "SymInitialize" );\r
- pSSO = (tSSO) GetProcAddress( g_hImagehlpDll, "SymSetOptions" );\r
- pSW = (tSW) GetProcAddress( g_hImagehlpDll, "StackWalk64" );\r
- pUDSN = (tUDSN) GetProcAddress( g_hImagehlpDll, "UnDecorateSymbolName" );\r
- pSLM = (tSLM) GetProcAddress( g_hImagehlpDll, "SymLoadModule64" );\r
-\r
- if ( pSC == NULL || pSFTA == NULL || pSGMB == NULL || pSGMI == NULL ||\r
- pSGO == NULL || pSGSFA == NULL || pSI == NULL || pSSO == NULL ||\r
- pSW == NULL || pUDSN == NULL || pSLM == NULL )\r
- {\r
- printf( "GetProcAddress(): some required function not found.\n" );\r
- FreeLibrary( g_hImagehlpDll );\r
- g_bInitialized = FALSE;\r
- return 1;\r
- }\r
-\r
- g_bInitialized = TRUE;\r
- InitializeCriticalSection(&g_csFileOpenClose);\r
- return 0;\r
-}\r
-\r
-// This function if NOT multi-threading capable\r
-// It should only be called from the main-Function!\r
-int InitAllocCheckWN(eAllocCheckOutput eOutput, LPCTSTR pszFileName, ULONG ulShowStackAtAlloc) {\r
- if (g_bInitialized) {\r
- return 2; // already initialized!\r
- }\r
- if (ulShowStackAtAlloc <= 3)\r
- g_ulShowStackAtAlloc = ulShowStackAtAlloc;\r
- else\r
- g_ulShowStackAtAlloc = 0;\r
-\r
- if (pszFileName != NULL) \r
- g_pszAllocLogName = _tcsdup(pszFileName);\r
- else\r
- g_pszAllocLogName = NULL;\r
-\r
- g_CallstackOutputType = eOutput;\r
-\r
-#ifdef _DEBUG\r
- AllocHashInit();\r
-\r
-#ifdef WITH_IMALLOC_SPY\r
- HRESULT hr;\r
- // erzeuge mein malloc-Spy object\r
- LPMALLOCSPY pMallocSpy = new CMallocSpy(); // wird später durch Release freigegeben\r
- if (pMallocSpy != NULL)\r
- {\r
- // CoInitilize(); // ??? Ist dies notwendig ?\r
- hr = CoRegisterMallocSpy(pMallocSpy);\r
- if FAILED(hr)\r
- {\r
- _tprintf(_T("\nCoRegisterMallocSpay failed with %.8x"), hr);\r
- }\r
- }\r
-#endif\r
-\r
- // save the previous alloc hook\r
- pfnOldCrtAllocHook = _CrtSetAllocHook(MyAllocHook);\r
-#endif\r
-\r
- return InitStackWalk();\r
-} // InitAllocCheckWN\r
-\r
-static TCHAR s_szExceptionLogFileName[_MAX_PATH] = _T("\\exceptions.log"); // default\r
-static BOOL s_bUnhandledExeptionFilterSet = FALSE;\r
-static LONG __stdcall CrashHandlerExceptionFilter(EXCEPTION_POINTERS* pExPtrs)\r
-{\r
- if (pExPtrs->ExceptionRecord->ExceptionCode == EXCEPTION_STACK_OVERFLOW)\r
- {\r
- static char MyStack[1024*128]; // be sure that we have enought space...\r
- // it assumes that DS and SS are the same!!! (this is the case for Win32)\r
- // change the stack only if the selectors are the same (this is the case for Win32)\r
- //__asm push offset MyStack[1024*128];\r
- //__asm pop esp;\r
- __asm mov eax,offset MyStack[1024*128];\r
- __asm mov esp,eax;\r
- }\r
-\r
- LONG lRet;\r
- lRet = StackwalkFilter(pExPtrs, /*EXCEPTION_CONTINUE_SEARCH*/EXCEPTION_EXECUTE_HANDLER, s_szExceptionLogFileName);\r
- TCHAR lString[500];\r
- _stprintf(lString,\r
- _T("*** Unhandled Exception!\n")\r
- _T(" ExpCode: 0x%8.8X\n")\r
- _T(" ExpFlags: %d\n")\r
- _T(" ExpAddress: 0x%8.8X\n")\r
- _T(" Please report!"),\r
- pExPtrs->ExceptionRecord->ExceptionCode,\r
- pExPtrs->ExceptionRecord->ExceptionFlags,\r
- pExPtrs->ExceptionRecord->ExceptionAddress);\r
- FatalAppExit(-1,lString);\r
- return lRet;\r
-}\r
-\r
-int InitAllocCheck(eAllocCheckOutput eOutput, BOOL bSetUnhandledExeptionFilter, ULONG ulShowStackAtAlloc) // will create the filename by itself\r
-{\r
- TCHAR szModName[_MAX_PATH];\r
- if (GetModuleFileName(NULL, szModName, sizeof(szModName)/sizeof(TCHAR)) != 0)\r
- {\r
- _tcscpy(s_szExceptionLogFileName, szModName);\r
- if (eOutput == ACOutput_XML)\r
- _tcscat(s_szExceptionLogFileName, _T(".exp.xml"));\r
- else\r
- _tcscat(s_szExceptionLogFileName, _T(".exp.log"));\r
-\r
- if (eOutput == ACOutput_XML)\r
- _tcscat(szModName, _T(".mem.xml-leaks"));\r
- else\r
- _tcscat(szModName, _T(".mem.log"));\r
- }\r
- else\r
- {\r
- if (eOutput == ACOutput_XML)\r
- _tcscpy(szModName, _T("\\mem-leaks.xml-leaks")); // default\r
- else\r
- _tcscpy(szModName, _T("\\mem-leaks.log")); // default\r
- }\r
-\r
- if ((bSetUnhandledExeptionFilter != FALSE) && (s_bUnhandledExeptionFilterSet == FALSE) )\r
- {\r
- // set global exception handler (for handling all unhandled exceptions)\r
- SetUnhandledExceptionFilter(CrashHandlerExceptionFilter);\r
- s_bUnhandledExeptionFilterSet = TRUE;\r
- }\r
-\r
- return InitAllocCheckWN(eOutput, szModName, ulShowStackAtAlloc);\r
-}\r
-\r
-\r
-// This function if NOT multi-threading capable\r
-// It should only be called from the main-Function!\r
-// Returns the number of bytes that are not freed (leaks)\r
-ULONG DeInitAllocCheck(void) {\r
- ULONG ulRet = 0;\r
- if (g_bInitialized) {\r
-\r
-#ifdef _DEBUG\r
- InterlockedIncrement(&g_lMallocCalled); // No deactivate MyAllocHook, because StackWalker will allocate some memory)\r
- ulRet = AllocHashDeinit(); // output the not freed memory\r
- // remove the hook and set the old one\r
- _CrtSetAllocHook(pfnOldCrtAllocHook);\r
-\r
-#ifdef WITH_IMALLOC_SPY\r
- CoRevokeMallocSpy();\r
-#endif\r
-\r
-#endif\r
-\r
- EnterCriticalSection(&g_csFileOpenClose); // wait until a running stack dump was created\r
- g_bInitialized = FALSE;\r
-\r
- // de-init symbol handler etc. (SymCleanup())\r
- if (pSC != NULL)\r
- pSC( GetCurrentProcess() );\r
- FreeLibrary( g_hImagehlpDll );\r
-\r
- LeaveCriticalSection(&g_csFileOpenClose);\r
- if (g_pszAllocLogName != NULL) {\r
- free(g_pszAllocLogName);\r
- g_pszAllocLogName = NULL;\r
- }\r
- if (g_fFile != NULL) {\r
- fclose(g_fFile);\r
- g_fFile = NULL;\r
- }\r
-\r
- DeleteCriticalSection(&g_csFileOpenClose);\r
- InterlockedDecrement(&g_lMallocCalled);\r
- }\r
-\r
- if (s_bUnhandledExeptionFilterSet != TRUE)\r
- {\r
- SetUnhandledExceptionFilter(NULL);\r
- s_bUnhandledExeptionFilterSet = FALSE;\r
- }\r
- return ulRet;\r
-} // DeInitAllocCheck\r
-\r
-\r
-\r
-void OnlyInstallUnhandeldExceptionFilter(eAllocCheckOutput eOutput)\r
-{\r
- if (s_bUnhandledExeptionFilterSet == FALSE)\r
- {\r
- TCHAR szModName[_MAX_PATH];\r
- if (GetModuleFileName(NULL, szModName, sizeof(szModName)/sizeof(TCHAR)) != 0)\r
- {\r
- _tcscpy(s_szExceptionLogFileName, szModName);\r
- if (eOutput == ACOutput_XML)\r
- _tcscat(s_szExceptionLogFileName, _T(".exp.xml"));\r
- else\r
- _tcscat(s_szExceptionLogFileName, _T(".exp.log"));\r
-\r
- if (eOutput == ACOutput_XML)\r
- _tcscat(szModName, _T(".mem.xml-leaks"));\r
- else\r
- _tcscat(szModName, _T(".mem.log"));\r
- }\r
- else\r
- {\r
- if (eOutput == ACOutput_XML)\r
- _tcscpy(szModName, _T("\\mem-leaks.xml-leaks")); // default\r
- else\r
- _tcscpy(szModName, _T("\\mem-leaks.log")); // default\r
- }\r
- // set it again; WARNING: this will override the setting for a possible AllocCheck-Setting\r
- g_CallstackOutputType = eOutput;\r
-\r
- // set global exception handler (for handling all unhandled exceptions)\r
- SetUnhandledExceptionFilter(CrashHandlerExceptionFilter);\r
- s_bUnhandledExeptionFilterSet = TRUE;\r
- }\r
-}\r
-\r
-\r
-\r
-static TCHAR *GetExpectionCodeText(DWORD dwExceptionCode) {\r
- switch(dwExceptionCode) {\r
- case EXCEPTION_ACCESS_VIOLATION: return _T("ACCESS VIOLATION");\r
- case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: return _T("ARRAY BOUNDS EXCEEDED");\r
- case EXCEPTION_BREAKPOINT: return _T("BREAKPOINT");\r
- case EXCEPTION_DATATYPE_MISALIGNMENT: return _T("DATATYPE MISALIGNMENT");\r
- case EXCEPTION_FLT_DENORMAL_OPERAND: return _T("FLT DENORMAL OPERAND");\r
- case EXCEPTION_FLT_DIVIDE_BY_ZERO: return _T("FLT DIVIDE BY ZERO");\r
- case EXCEPTION_FLT_INEXACT_RESULT: return _T("FLT INEXACT RESULT");\r
- case EXCEPTION_FLT_INVALID_OPERATION: return _T("FLT INVALID OPERATION");\r
- case EXCEPTION_FLT_OVERFLOW: return _T("FLT OVERFLOW");\r
- case EXCEPTION_FLT_STACK_CHECK: return _T("FLT STACK CHECK");\r
- case EXCEPTION_FLT_UNDERFLOW: return _T("FLT UNDERFLOW");\r
- case EXCEPTION_ILLEGAL_INSTRUCTION: return _T("ILLEGAL INSTRUCTION");\r
- case EXCEPTION_IN_PAGE_ERROR: return _T("IN PAGE ERROR");\r
- case EXCEPTION_INT_DIVIDE_BY_ZERO: return _T("INT DIVIDE BY ZERO");\r
- case EXCEPTION_INT_OVERFLOW: return _T("INT OVERFLOW");\r
- case EXCEPTION_INVALID_DISPOSITION: return _T("INVALID DISPOSITION");\r
- case EXCEPTION_NONCONTINUABLE_EXCEPTION: return _T("NONCONTINUABLE EXCEPTION");\r
- case EXCEPTION_PRIV_INSTRUCTION: return _T("PRIV INSTRUCTION");\r
- case EXCEPTION_SINGLE_STEP: return _T("SINGLE STEP");\r
- case EXCEPTION_STACK_OVERFLOW: return _T("STACK OVERFLOW");\r
- case DBG_CONTROL_C : return _T("DBG CONTROL C ");\r
- default:\r
- return _T("<unkown exception>");\r
- }\r
-} // GetExpectionCodeText\r
-\r
-// Function is not multi-threading safe, because of static char!\r
-static TCHAR *GetAdditionalExpectionCodeText(PEXCEPTION_RECORD pExceptionRecord) {\r
- static TCHAR szTemp[100];\r
-\r
- switch(pExceptionRecord->ExceptionCode) {\r
- case EXCEPTION_ACCESS_VIOLATION:\r
- if (pExceptionRecord->NumberParameters == 2) {\r
- switch(pExceptionRecord->ExceptionInformation[0]) {\r
- case 0: // read attempt\r
- _stprintf(szTemp, _T(" read attempt to address 0x%8.8X "), pExceptionRecord->ExceptionInformation[1]);\r
- return szTemp;\r
- case 1: // write attempt\r
- _stprintf(szTemp, _T(" write attempt to address 0x%8.8X "), pExceptionRecord->ExceptionInformation[1]);\r
- return szTemp;\r
- default:\r
- return _T("");\r
- }\r
- } // if (pExceptionRecord->NumberParameters == 2)\r
- return _T("");\r
- default:\r
- return _T("");\r
- } // switch(pExceptionRecord->ExceptionCode)\r
-} // GetAdditionalExpectionCodeText\r
-\r
-std::string SimpleXMLEncode(PCSTR szText)\r
-{\r
- std::string szRet;\r
-\r
- for (size_t i=0; i<strlen(szText); i++)\r
- {\r
- switch(szText[i])\r
- {\r
- case '&':\r
- szRet.append("&");\r
- break;\r
- case '<':\r
- szRet.append("<");\r
- break;\r
- case '>':\r
- szRet.append(">");\r
- break;\r
- case '"':\r
- szRet.append(""");\r
- break;\r
- case '\'':\r
- szRet.append("'");\r
- break;\r
- default:\r
- szRet += szText[i];\r
- }\r
- }\r
- return szRet;\r
-}\r
-\r
-\r
-// #################################################################################\r
-// #################################################################################\r
-// Here the Stackwalk-Part begins.\r
-// Some of the code is from an example from a book \r
-// But I couldn´t find the reference anymore... sorry...\r
-// If someone knowns, please let me know...\r
-// #################################################################################\r
-// #################################################################################\r
-\r
-\r
-// if you use C++ exception handling: install a translator function\r
-// with set_se_translator(). In the context of that function (but *not*\r
-// afterwards), you can either do your stack dump, or save the CONTEXT\r
-// record as a local copy. Note that you must do the stack sump at the\r
-// earliest opportunity, to avoid the interesting stackframes being gone\r
-// by the time you do the dump.\r
-\r
-// status: \r
-// - EXCEPTION_CONTINUE_SEARCH: exception wird weitergereicht\r
-// - EXCEPTION_CONTINUE_EXECUTION: \r
-// - EXCEPTION_EXECUTE_HANDLER:\r
-DWORD StackwalkFilter( EXCEPTION_POINTERS *ep, DWORD status, LPCTSTR pszLogFile)\r
-{\r
- HANDLE hThread;\r
- FILE *fFile = stdout; // default to stdout\r
-\r
- if (pszLogFile != NULL) { // a filename is provided\r
- // Open the logfile\r
- fFile = _tfopen(pszLogFile, _T("a"));\r
- if (fFile != NULL) { // Is the file too big?\r
- long size;\r
- fseek(fFile, 0, SEEK_END);\r
- size = ftell(fFile); // Get the size of the file\r
- if (size >= LOG_FILE_MAX_SIZE) {\r
- TCHAR *pszTemp = (TCHAR*) malloc(MAX_PATH);\r
- // It is too big...\r
- fclose(fFile);\r
- _tcscpy(pszTemp, pszLogFile);\r
- _tcscat(pszTemp, _T(".old"));\r
- _tremove(pszTemp); // Remove an old file, if exists\r
- _trename(pszLogFile, pszTemp); // rename the actual file\r
- fFile = _tfopen(pszLogFile, _T("w")); // create a new file\r
- free(pszTemp);\r
- }\r
- }\r
- } // if (pszLogFile != NULL) \r
- if (fFile == NULL) {\r
- fFile = stdout;\r
- }\r
-\r
- // Write infos about the exception\r
- if (g_CallstackOutputType == ACOutput_XML)\r
- {\r
- _ftprintf(fFile, _T("<EXCEPTION code=\"%8.8X\" addr=\"%8.8X\" "), \r
- ep->ExceptionRecord->ExceptionCode,\r
- ep->ExceptionRecord->ExceptionAddress);\r
- WriteDateTime(fFile, TRUE);\r
- _ftprintf(fFile, _T("code_desc=\"%s\" more_desc=\"%s\">\n"), GetExpectionCodeText(ep->ExceptionRecord->ExceptionCode),\r
- GetAdditionalExpectionCodeText(ep->ExceptionRecord));\r
- }\r
- else\r
- {\r
- _ftprintf(fFile, _T("######## EXCEPTION: 0x%8.8X at address: 0x%8.8X"), \r
- ep->ExceptionRecord->ExceptionCode,\r
- ep->ExceptionRecord->ExceptionAddress);\r
- _ftprintf(fFile, _T(": %s %s\n"), GetExpectionCodeText(ep->ExceptionRecord->ExceptionCode),\r
- GetAdditionalExpectionCodeText(ep->ExceptionRecord));\r
- }\r
-\r
- DuplicateHandle( GetCurrentProcess(), GetCurrentThread(),\r
- GetCurrentProcess(), &hThread, 0, false, DUPLICATE_SAME_ACCESS );\r
- ShowStack( hThread, *(ep->ContextRecord), fFile);\r
- CloseHandle( hThread );\r
-\r
- if (g_CallstackOutputType == ACOutput_XML)\r
- _ftprintf(fFile, _T("</EXCEPTION>\n"));\r
-\r
- fclose(fFile);\r
-\r
- return status;\r
-} // StackwalkFilter\r
-\r
-void ShowStack( HANDLE hThread, CONTEXT& c, LPCTSTR pszLogFile)\r
-{\r
- FILE *fFile = stdout; // default to stdout\r
-\r
- if (pszLogFile != NULL) { // a filename is available\r
- // Open the logfile\r
- fFile = _tfopen(pszLogFile, _T("a"));\r
- if (fFile != NULL) { // Is the file too big?\r
- long size;\r
- fseek(fFile, 0, SEEK_END);\r
- size = ftell(fFile); // Get the size of the file\r
- if (size >= LOG_FILE_MAX_SIZE) {\r
- TCHAR *pszTemp = (TCHAR*) malloc(MAX_PATH);\r
- // It is too big...\r
- fclose(fFile);\r
- _tcscpy(pszTemp, pszLogFile);\r
- _tcscat(pszTemp, _T(".old"));\r
- _tremove(pszTemp); // Remove an old file, if exists\r
- _trename(pszLogFile, pszTemp); // rename the actual file\r
- fFile = _tfopen(pszLogFile, _T("w")); // open new file\r
- free(pszTemp);\r
- }\r
- }\r
- } // if (pszLogFile != NULL) \r
- if (fFile == NULL) {\r
- fFile = stdout;\r
- }\r
-\r
- ShowStack( hThread, c, fFile);\r
-\r
- fclose(fFile);\r
-}\r
-\r
-\r
-static void ShowStack( HANDLE hThread, CONTEXT& c, FILE *fLogFile) {\r
- ShowStackRM(hThread, c, fLogFile, NULL, GetCurrentProcess());\r
-}\r
-\r
-static void ShowStackRM( HANDLE hThread, CONTEXT& c, FILE *fLogFile, PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryFunction, HANDLE hSWProcess) {\r
- // normally, call ImageNtHeader() and use machine info from PE header\r
- // but we assume that it is an I386 image...\r
- DWORD imageType = IMAGE_FILE_MACHINE_I386;\r
- HANDLE hProcess = GetCurrentProcess(); // hProcess normally comes from outside but we only do the stackdump in our own process\r
- int frameNum; // counts walked frames\r
- DWORD64 offsetFromSymbol; // tells us how far from the symbol we were\r
- DWORD offsetFromLine; // tells us how far from the line we were\r
- DWORD symOptions; // symbol handler settings\r
-\r
- static IMAGEHLP_SYMBOL64 *pSym = NULL;\r
- char undName[MAXNAMELEN]; // undecorated name\r
- char undFullName[MAXNAMELEN]; // undecorated name with all shenanigans\r
- IMAGEHLP_MODULE64 Module;\r
- IMAGEHLP_LINE64 Line;\r
- BOOL bXMLTagWrote;\r
-\r
- std::string symSearchPath;\r
-\r
- static bool bFirstTime = TRUE;\r
-\r
- // If no logfile is present, outpur to "stdout"\r
- if (fLogFile == NULL) {\r
- fLogFile = stdout;\r
- }\r
-\r
- STACKFRAME64 s; // in/out stackframe\r
- memset( &s, '\0', sizeof s );\r
-\r
- if ( (g_bInitialized == FALSE) && (bFirstTime == TRUE) ) {\r
- InitStackWalk();\r
- }\r
-\r
- if (g_bInitialized == FALSE)\r
- {\r
- // Could not init!!!!\r
- bFirstTime = FALSE;\r
- _ftprintf(fLogFile, _T("%lu: Stackwalker not initialized (or was not able to initialize)!\n"), g_dwShowCount);\r
- return;\r
- }\r
-\r
-// Critical section begin...\r
- EnterCriticalSection(&g_csFileOpenClose);\r
-\r
- InterlockedIncrement((long*) &g_dwShowCount); // erhöhe counter\r
-\r
-\r
- // NOTE: normally, the exe directory and the current directory should be taken\r
- // from the target process. The current dir would be gotten through injection\r
- // of a remote thread; the exe fir through either ToolHelp32 or PSAPI.\r
-\r
- if (pSym == NULL) {\r
- pSym = (IMAGEHLP_SYMBOL64 *) malloc( IMGSYMLEN + MAXNAMELEN );\r
- if (!pSym) goto cleanup; // not enough memory...\r
- }\r
-\r
- if (g_CallstackOutputType != ACOutput_XML)\r
- {\r
- _ftprintf(fLogFile, _T("%lu: "), g_dwShowCount);\r
- WriteDateTime(fLogFile);\r
- _ftprintf(fLogFile, _T("\n"));\r
- }\r
-\r
-\r
- if (bFirstTime) {\r
-\r
- CHAR *tt, *p;\r
-\r
- tt = (CHAR*) malloc(sizeof(CHAR) * TTBUFLEN); // Get the temporary buffer\r
- if (!tt) goto cleanup; // not enough memory...\r
-\r
- // build symbol search path from:\r
- symSearchPath = "";\r
- // current directory\r
- if ( GetCurrentDirectoryA( TTBUFLEN, tt ) )\r
- symSearchPath += tt + std::string( ";" );\r
- // dir with executable\r
- if ( GetModuleFileNameA( 0, tt, TTBUFLEN ) )\r
- {\r
- for ( p = tt + strlen( tt ) - 1; p >= tt; -- p )\r
- {\r
- // locate the rightmost path separator\r
- if ( *p == '\\' || *p == '/' || *p == ':' )\r
- break;\r
- }\r
- // if we found one, p is pointing at it; if not, tt only contains\r
- // an exe name (no path), and p points before its first byte\r
- if ( p != tt ) // path sep found?\r
- {\r
- if ( *p == ':' ) // we leave colons in place\r
- ++ p;\r
- *p = '\0'; // eliminate the exe name and last path sep\r
- symSearchPath += tt + std::string( ";" );\r
- }\r
- }\r
- // environment variable _NT_SYMBOL_PATH\r
- if ( GetEnvironmentVariableA( "_NT_SYMBOL_PATH", tt, TTBUFLEN ) )\r
- symSearchPath += tt + std::string( ";" );\r
- // environment variable _NT_ALTERNATE_SYMBOL_PATH\r
- if ( GetEnvironmentVariableA( "_NT_ALTERNATE_SYMBOL_PATH", tt, TTBUFLEN ) )\r
- symSearchPath += tt + std::string( ";" );\r
- // environment variable SYSTEMROOT\r
- if ( GetEnvironmentVariableA( "SYSTEMROOT", tt, TTBUFLEN ) )\r
- symSearchPath += tt + std::string( ";" );\r
-\r
-\r
-\r
- if ( symSearchPath.size() > 0 ) // if we added anything, we have a trailing semicolon\r
- symSearchPath = symSearchPath.substr( 0, symSearchPath.size() - 1 );\r
-\r
- // why oh why does SymInitialize() want a writeable string?\r
- strncpy( tt, symSearchPath.c_str(), TTBUFLEN );\r
- tt[TTBUFLEN - 1] = '\0'; // if strncpy() overruns, it doesn't add the null terminator\r
-\r
- // init symbol handler stuff (SymInitialize())\r
- if ( ! pSI( hProcess, tt, false ) )\r
- {\r
- if (g_CallstackOutputType != ACOutput_XML)\r
- _ftprintf(fLogFile, _T("%lu: SymInitialize(): GetLastError = %lu\n"), g_dwShowCount, gle );\r
- if (tt) free( tt );\r
- goto cleanup;\r
- }\r
-\r
- // SymGetOptions()\r
- symOptions = pSGO();\r
- symOptions |= SYMOPT_LOAD_LINES;\r
- symOptions &= ~SYMOPT_UNDNAME;\r
- symOptions &= ~SYMOPT_DEFERRED_LOADS;\r
- pSSO( symOptions ); // SymSetOptions()\r
-\r
- // Enumerate modules and tell dbghlp.dll about them.\r
- // On NT, this is not necessary, but it won't hurt.\r
- EnumAndLoadModuleSymbols( hProcess, GetCurrentProcessId(), fLogFile );\r
-\r
- if (tt) \r
- free( tt );\r
- } // bFirstTime = TRUE\r
- bFirstTime = FALSE;\r
-\r
- // init STACKFRAME for first call\r
- // Notes: AddrModeFlat is just an assumption. I hate VDM debugging.\r
- // Notes: will have to be #ifdef-ed for Alphas; MIPSes are dead anyway,\r
- // and good riddance.\r
- s.AddrPC.Offset = c.Eip;\r
- s.AddrPC.Mode = AddrModeFlat;\r
- s.AddrFrame.Offset = c.Ebp;\r
- s.AddrFrame.Mode = AddrModeFlat;\r
- s.AddrStack.Offset = c.Ebp;\r
- s.AddrStack.Mode = AddrModeFlat;\r
-\r
- memset( pSym, '\0', IMGSYMLEN + MAXNAMELEN );\r
- pSym->SizeOfStruct = IMGSYMLEN;\r
- pSym->MaxNameLength = MAXNAMELEN;\r
-\r
- memset( &Line, '\0', sizeof Line );\r
- Line.SizeOfStruct = sizeof Line;\r
-\r
- memset( &Module, '\0', sizeof Module );\r
- Module.SizeOfStruct = sizeof Module;\r
-\r
- for ( frameNum = 0; ; ++ frameNum )\r
- {\r
- // get next stack frame (StackWalk64(), SymFunctionTableAccess64(), SymGetModuleBase64())\r
- // if this returns ERROR_INVALID_ADDRESS (487) or ERROR_NOACCESS (998), you can\r
- // assume that either you are done, or that the stack is so hosed that the next\r
- // deeper frame could not be found.\r
- // CONTEXT need not to be suplied if imageTyp is IMAGE_FILE_MACHINE_I386!\r
- if ( ! pSW( imageType, hSWProcess, hThread, &s, NULL, ReadMemoryFunction, pSFTA, pSGMB, NULL ) )\r
- break;\r
-\r
- bXMLTagWrote = FALSE;\r
-\r
- if (g_CallstackOutputType == ACOutput_Advanced)\r
- _ftprintf(fLogFile, _T("\n%lu: %3d"), g_dwShowCount, frameNum);\r
- if ( s.AddrPC.Offset == 0 )\r
- {\r
- // Special case: If we are here, we have no valid callstack entry!\r
- switch(g_CallstackOutputType)\r
- {\r
- case ACOutput_Simple:\r
- _ftprintf(fLogFile, _T("%lu: (-nosymbols- PC == 0)\n"), g_dwShowCount);\r
- break;\r
- case ACOutput_Advanced:\r
- _ftprintf(fLogFile, _T(" (-nosymbols- PC == 0)\n"));\r
- break;\r
- case ACOutput_XML:\r
- // TODO: ....\r
- _ftprintf(fLogFile, _T("<STACKENTRY decl=\"(-nosymbols- PC == 0)\"/>\n"));\r
- break;\r
- }\r
- }\r
- else\r
- {\r
- // we seem to have a valid PC\r
- undName[0] = 0;\r
- undFullName[0] = 0;\r
- offsetFromSymbol = 0;\r
- // show procedure info (SymGetSymFromAddr())\r
- if ( ! pSGSFA( hProcess, s.AddrPC.Offset, &offsetFromSymbol, pSym ) )\r
- {\r
- if (g_CallstackOutputType == ACOutput_Advanced)\r
- {\r
- if ( gle != 487 )\r
- _ftprintf(fLogFile, _T(" SymGetSymFromAddr(): GetLastError = %lu\n"), gle );\r
- else\r
- _ftprintf(fLogFile, _T("\n"));\r
- }\r
- }\r
- else\r
- {\r
- // UnDecorateSymbolName()\r
- pUDSN( pSym->Name, undName, MAXNAMELEN, UNDNAME_NAME_ONLY );\r
- pUDSN( pSym->Name, undFullName, MAXNAMELEN, UNDNAME_COMPLETE );\r
- if (g_CallstackOutputType == ACOutput_Advanced)\r
- {\r
- if (strlen(undName) > 0)\r
- fprintf(fLogFile, " %s %+ld bytes\n", undName, (long) offsetFromSymbol );\r
- else\r
- {\r
- fprintf(fLogFile, " Sig: %s %+ld bytes\n", pSym->Name, (long) offsetFromSymbol );\r
- strcpy(undName, pSym->Name);\r
- }\r
- fprintf(fLogFile, "%lu: Decl: %s\n", g_dwShowCount, undFullName );\r
- }\r
- }\r
- //if (g_CallstackOutputType == ACOutput_XML)\r
- // fprintf(fLogFile, "decl=\"%s\" decl_offset=\"%+ld\" ", SimpleXMLEncode(undName).c_str(), (long) offsetFromSymbol);\r
-\r
- // show line number info, NT5.0-method (SymGetLineFromAddr())\r
- offsetFromLine = 0;\r
- if ( pSGLFA != NULL )\r
- { // yes, we have SymGetLineFromAddr()\r
- if ( ! pSGLFA( hProcess, s.AddrPC.Offset, &offsetFromLine, &Line ) )\r
- {\r
- if ( (gle != 487) && (frameNum > 0) ) // ignore error for first frame\r
- {\r
- if (g_CallstackOutputType == ACOutput_XML)\r
- {\r
- _ftprintf(fLogFile, _T("<STACKENTRY "));\r
- bXMLTagWrote = TRUE;\r
- fprintf(fLogFile, "decl=\"%s\" decl_offset=\"%+ld\" ", SimpleXMLEncode(undName).c_str(), (long) offsetFromSymbol);\r
- _ftprintf(fLogFile, _T("srcfile=\"SymGetLineFromAddr(): GetLastError = %lu\" "), gle);\r
- }\r
- else\r
- _ftprintf(fLogFile, _T("%lu: SymGetLineFromAddr(): GetLastError = %lu\n"), g_dwShowCount, gle );\r
- }\r
- }\r
- else\r
- {\r
- switch(g_CallstackOutputType)\r
- {\r
- case ACOutput_Advanced:\r
- fprintf(fLogFile, "%lu: Line: %s(%lu) %+ld bytes\n", g_dwShowCount,\r
- Line.FileName, Line.LineNumber, offsetFromLine );\r
- break;\r
- case ACOutput_Simple:\r
- fprintf(fLogFile, "%lu: %s(%lu) %+ld bytes (%s)\n", g_dwShowCount,\r
- Line.FileName, Line.LineNumber, offsetFromLine, undName);\r
- break;\r
- case ACOutput_XML:\r
- _ftprintf(fLogFile, _T("<STACKENTRY "));\r
- bXMLTagWrote = TRUE;\r
- fprintf(fLogFile, "decl=\"%s\" decl_offset=\"%+ld\" ", SimpleXMLEncode(undName).c_str(), (long) offsetFromSymbol);\r
- fprintf(fLogFile, "srcfile=\"%s\" line=\"%lu\" line_offset=\"%+ld\" ", \r
- SimpleXMLEncode(Line.FileName).c_str(), Line.LineNumber, offsetFromLine, undName);\r
- break;\r
- }\r
- }\r
- } // yes, we have SymGetLineFromAddr()\r
-\r
- // show module info (SymGetModuleInfo())\r
- if ( (g_CallstackOutputType == ACOutput_Advanced) || (g_CallstackOutputType == ACOutput_XML) )\r
- {\r
- if ( ! pSGMI( hProcess, s.AddrPC.Offset, &Module ) )\r
- {\r
- if (g_CallstackOutputType == ACOutput_Advanced)\r
- _ftprintf(fLogFile, _T("%lu: SymGetModuleInfo): GetLastError = %lu\n"), g_dwShowCount, gle );\r
- }\r
- else\r
- { // got module info OK\r
- char ty[80];\r
- switch ( Module.SymType )\r
- {\r
- case SymNone:\r
- strcpy( ty, "-nosymbols-" );\r
- break;\r
- case SymCoff:\r
- strcpy( ty, "COFF" );\r
- break;\r
- case SymCv:\r
- strcpy( ty, "CV" );\r
- break;\r
- case SymPdb:\r
- strcpy( ty, "PDB" );\r
- break;\r
- case SymExport:\r
- strcpy( ty, "-exported-" );\r
- break;\r
- case SymDeferred:\r
- strcpy( ty, "-deferred-" );\r
- break;\r
- case SymSym:\r
- strcpy( ty, "SYM" );\r
- break;\r
-#if API_VERSION_NUMBER >= 9\r
- case SymDia:\r
- strcpy( ty, "DIA" );\r
- break;\r
-#endif\r
- default:\r
- _snprintf( ty, sizeof ty, "symtype=%ld", (long) Module.SymType );\r
- break;\r
- }\r
-\r
- if (g_CallstackOutputType == ACOutput_XML)\r
- {\r
- // now, check if the XML-Entry is written...\r
- if (bXMLTagWrote == FALSE) \r
- {\r
- _ftprintf(fLogFile, _T("<STACKENTRY "));\r
- bXMLTagWrote = TRUE;\r
- fprintf(fLogFile, "decl=\"%s\" decl_offset=\"%+ld\" ", SimpleXMLEncode(undName).c_str(), (long) offsetFromSymbol);\r
- _ftprintf(fLogFile, _T("srcfile=\"\" "));\r
- bXMLTagWrote = TRUE;\r
- }\r
- }\r
-\r
- if (g_CallstackOutputType == ACOutput_Advanced)\r
- {\r
- fprintf(fLogFile, "%lu: Mod: %s, base: %08lxh\n", g_dwShowCount,\r
- Module.ModuleName, Module.BaseOfImage );\r
- if (Module.SymType == SymNone) { // Gebe nur aus, wenn keine Symbole vorhanden sind!\r
- _ftprintf(fLogFile, _T("%lu: Offset: 0x%8.8x\n"), g_dwShowCount, s.AddrPC.Offset);\r
- fprintf(fLogFile, "%lu: Sym: type: %s, file: %s\n", g_dwShowCount,\r
- ty, Module.LoadedImageName );\r
- }\r
- }\r
- else\r
- {\r
- // XML:\r
- if (bXMLTagWrote == TRUE)\r
- fprintf(fLogFile, "module=\"%s\" base=\"%08lx\" ", Module.ModuleName, Module.BaseOfImage);\r
- }\r
- } // got module info OK\r
- }\r
- if ( (g_CallstackOutputType == ACOutput_XML) && (bXMLTagWrote == TRUE) )\r
- _ftprintf(fLogFile, _T("/>\n")); // terminate the XML node\r
-\r
- } // we seem to have a valid PC\r
-\r
- // no return address means no deeper stackframe\r
- if ( s.AddrReturn.Offset == 0 )\r
- {\r
- // avoid misunderstandings in the printf() following the loop\r
- SetLastError( 0 );\r
- break;\r
- }\r
-\r
- } // for ( frameNum )\r
-\r
- if ( (g_CallstackOutputType != ACOutput_XML) && (gle != 0) )\r
- _ftprintf(fLogFile, _T("\n%lu: StackWalk(): GetLastError = %lu\n"), g_dwShowCount, gle );\r
-\r
-cleanup:\r
- //if (pSym) free( pSym );\r
- if (fLogFile) {\r
- _ftprintf(fLogFile, _T("\n\n"));\r
- if (g_dwShowCount % 1000)\r
- fflush(fLogFile);\r
- }\r
-\r
- LeaveCriticalSection(&g_csFileOpenClose);\r
-\r
-\r
-// Critical section end...\r
-} // ShowStackRM\r
-\r
-#pragma warning(pop)\r
+/*////////////////////////////////////////////////////////////////////////////
+ * 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"
+
+#pragma warning(push)
+#pragma warning(disable : 4100)
+#pragma warning(disable : 4996)
+#pragma warning(disable : 4189)
+#pragma warning(disable : 4245)
+#pragma warning(disable : 4701)
+
+// 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; // base load address of module
+ DWORD ImageSize; // virtual size of the loaded module
+ DWORD TimeDateStamp; // date/time stamp from pe header
+ DWORD CheckSum; // checksum from the pe header
+ DWORD NumSyms; // number of symbols in the symbol table
+ SYM_TYPE SymType; // type of symbols loaded
+ CHAR ModuleName[32]; // module name
+ CHAR ImageName[256]; // image name
+ CHAR LoadedImageName[256]; // symbol file name
+} IMAGEHLP_MODULE64, *PIMAGEHLP_MODULE64;
+
+typedef struct _IMAGEHLP_SYMBOL64 {
+ DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_SYMBOL64)
+ DWORD64 Address; // virtual address including dll base address
+ DWORD Size; // estimated size of symbol, can be zero
+ DWORD Flags; // info about the symbols, see the SYMF defines
+ DWORD MaxNameLength; // maximum size of symbol name in 'Name'
+ CHAR Name[1]; // symbol name (null terminated string)
+} IMAGEHLP_SYMBOL64, *PIMAGEHLP_SYMBOL64;
+
+typedef struct _tagADDRESS64 {
+ DWORD64 Offset;
+ WORD Segment;
+ ADDRESS_MODE Mode;
+} ADDRESS64, *LPADDRESS64;
+
+typedef struct _KDHELP64 {
+
+ //
+ // address of kernel thread object, as provided in the
+ // WAIT_STATE_CHANGE packet.
+ //
+ DWORD64 Thread;
+
+ //
+ // offset in thread object to pointer to the current callback frame
+ // in kernel stack.
+ //
+ DWORD ThCallbackStack;
+
+ //
+ // offset in thread object to pointer to the current callback backing
+ // store frame in kernel stack.
+ //
+ DWORD ThCallbackBStore;
+
+ //
+ // offsets to values in frame:
+ //
+ // address of next callback frame
+ DWORD NextCallback;
+
+ // address of saved frame pointer (if applicable)
+ DWORD FramePointer;
+
+
+ //
+ // Address of the kernel function that calls out to user mode
+ //
+ DWORD64 KiCallUserMode;
+
+ //
+ // Address of the user mode dispatcher function
+ //
+ DWORD64 KeUserCallbackDispatcher;
+
+ //
+ // Lowest kernel mode address
+ //
+ DWORD64 SystemRangeStart;
+
+ DWORD64 Reserved[8];
+
+} KDHELP64, *PKDHELP64;
+
+
+typedef struct _tagSTACKFRAME64 {
+ ADDRESS64 AddrPC; // program counter
+ ADDRESS64 AddrReturn; // return address
+ ADDRESS64 AddrFrame; // frame pointer
+ ADDRESS64 AddrStack; // stack pointer
+ ADDRESS64 AddrBStore; // backing store pointer
+ PVOID FuncTableEntry; // pointer to pdata/fpo or NULL
+ DWORD64 Params[4]; // possible arguments to the function
+ BOOL Far; // WOW far call
+ BOOL Virtual; // is this a virtual frame?
+ DWORD64 Reserved[3];
+ KDHELP64 KdHelp;
+} STACKFRAME64, *LPSTACKFRAME64;
+
+typedef
+PVOID
+(__stdcall *PFUNCTION_TABLE_ACCESS_ROUTINE64)(
+ HANDLE hProcess,
+ DWORD64 AddrBase
+ );
+
+typedef
+DWORD64
+(__stdcall *PGET_MODULE_BASE_ROUTINE64)(
+ HANDLE hProcess,
+ DWORD64 Address
+ );
+
+typedef
+DWORD64
+(__stdcall *PTRANSLATE_ADDRESS_ROUTINE64)(
+ HANDLE hProcess,
+ HANDLE hThread,
+ LPADDRESS64 lpaddr
+ );
+#endif
+// #############################################################################################
+
+
+
+// Forward definitions of functions:
+static void ShowStackRM( HANDLE hThread, CONTEXT& c, FILE *fLogFile, PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryFunction, HANDLE hProcess);
+static void ShowStack( HANDLE hThread, CONTEXT& c, FILE *fLogFile);
+
+//static void AllocHashOut(FILE*);
+static ULONG AllocHashOutLeaks(FILE*);
+
+
+
+// Globale Vars:
+static TCHAR *g_pszAllocLogName = NULL;
+static FILE *g_fFile = NULL;
+
+// AllocCheckFileOpen
+// Checks if the log-file is already opened
+// if not, try to open file (append or create if not exists)
+// if open failed, redirect output to stdout
+static void AllocCheckFileOpen(bool bAppend = true) {
+ // is the File already open? If not open it...
+ if (g_fFile == NULL)
+ if (g_pszAllocLogName != NULL)
+ {
+ if (bAppend == false)
+ g_fFile = _tfopen(g_pszAllocLogName, _T("w"));
+ else
+ g_fFile = _tfopen(g_pszAllocLogName, _T("a"));
+ }
+ if (g_fFile == NULL)
+ g_fFile = stdout;
+}
+
+// Write Date/Time to specified file (will also work after 2038)
+static void WriteDateTime(FILE *fFile, BOOL asXMLAttrs = FALSE) {
+ TCHAR pszTemp[11], pszTemp2[11];
+
+ if (fFile != NULL) {
+ _tstrdate( pszTemp );
+ _tstrtime( pszTemp2 );
+ if (asXMLAttrs == FALSE)
+ _ftprintf(fFile, _T("%s %s"), pszTemp, pszTemp2 ); // also ok after year 2038 (asctime is NOT ok)
+ else
+ _ftprintf(fFile, _T("date=\"%s\" time=\"%s\" "), pszTemp, pszTemp2 ); // also ok after year 2038 (asctime is NOT ok)
+ }
+} // WriteDateTime
+
+
+/*******************************************************************************
+ * Hash-Tabelle
+ *******************************************************************************/
+// Memory for the EIP-Address (is used by the ShowStack-method)
+#define MAX_EIP_LEN_BUF 4
+
+#define ALLOC_ENTRY_NOT_FOUND 0xFFFFFFFF
+
+typedef struct AllocHashEntryType {
+ long lRequestID; // RequestID from CRT (if 0, then this entry is empty)
+ size_t nDataSize; // Size of the allocated memory
+ char cRemovedFlag; // 0 => memory was not yet released
+ struct AllocHashEntryType *Next;
+ // Callstack for EIP
+ DWORD dwEIPOffset;
+ DWORD dwEIPLen;
+ char pcEIPAddr[MAX_EIP_LEN_BUF];
+ // Callstack for ESP
+ DWORD dwESPOffset;
+ DWORD dwESPLen;
+ char pcESPAddr[MAX_ESP_LEN_BUF];
+} AllocHashEntryType;
+
+static AllocHashEntryType AllocHashTable[ALLOC_HASH_ENTRIES];
+static ULONG AllocHashEntries = 0;
+static ULONG AllocHashCollisions = 0;
+static ULONG AllocHashFreed = 0;
+static ULONG AllocHashMaxUsed = 0; // maximal number of concurrent entries
+static ULONG AllocHashCurrentCount = 0;
+
+static ULONG AllocHashMaxCollisions = 0;
+static ULONG AllocHashCurrentCollisions = 0;
+
+// ##########################################################################################
+#ifdef WITH_IMALLOC_SPY
+// eigene Tabelle für die IMallocs:
+typedef struct IMallocHashEntryType {
+ void *pData; // Key-Word
+ size_t nDataSize; // größe des Datenblocks (optional)
+ char cRemovedFlag; // 0 => nicht wurde noch nicht freigegeben
+ struct IMallocHashEntryType *Next;
+ // Callstack für EIP
+ DWORD dwEIPOffset;
+ DWORD dwEIPLen;
+ char pcEIPAddr[MAX_EIP_LEN_BUF];
+ // Callstack für ESP
+ DWORD dwESPOffset;
+ DWORD dwESPLen;
+ char pcESPAddr[MAX_ESP_LEN_BUF];
+} IMallocHashEntryType;
+
+static IMallocHashEntryType IMallocHashTable[ALLOC_HASH_ENTRIES];
+
+static ULONG IMallocHashEntries = 0;
+static ULONG IMallocHashCollisions = 0;
+static ULONG IMallocHashFreed = 0;
+static ULONG IMallocHashMaxUsed = 0; // maximal number of concurrent entries
+static ULONG IMallocHashCurrentCount = 0;
+
+static ULONG IMallocHashMaxCollisions = 0;
+static ULONG IMallocHashCurrentCollisions = 0;
+
+
+//static void AllocHashOut(FILE*);
+static ULONG IMallocHashOutLeaks(FILE*);
+
+// AllocHashFunction
+// Die eigentliche Hash-Funktion (hier ganz simpel)
+static ULONG IMallocHashFunction(void *pData) {
+ ULONG ulTemp;
+ DWORD dwPointer = (DWORD) pData;
+
+ // relativ simpler Mechanismus für die Hash-Funktion,
+ // mir ist nur nix besseres eingefallen...
+ ulTemp = dwPointer % ALLOC_HASH_ENTRIES;
+
+ _ASSERTE( (ulTemp >= 0) && (ulTemp < ALLOC_HASH_ENTRIES) );
+
+ return ulTemp;
+} // AllocHashFunction
+
+// IMallocHashInsert
+// pData: Key-Word (Pointer to address)
+// pContext: Context-Record, for retrieving Callstack (EIP and EBP is only needed)
+// nDataSize: How many bytes
+void IMallocHashInsert(void *pData, CONTEXT &Context, size_t nDataSize) {
+ ULONG HashIdx;
+ IMallocHashEntryType *pHashEntry;
+
+ // ermittle Statistische Werte
+ IMallocHashEntries++;
+ IMallocHashCurrentCount++;
+ if (IMallocHashCurrentCount > IMallocHashMaxUsed)
+ IMallocHashMaxUsed = IMallocHashCurrentCount;
+
+ // ermittle den Hash-Wert
+ HashIdx = IMallocHashFunction(pData);
+
+ // Eintrag darf nicht größer als die Hash-Tabelle sein
+ _ASSERTE(HashIdx < ALLOC_HASH_ENTRIES);
+
+ pHashEntry = &IMallocHashTable[HashIdx];
+ if (pHashEntry->pData == 0) {
+ // es ist noch kein Eintrag da
+ }
+ else {
+ //Statistische Daten:
+ IMallocHashCollisions++;
+ IMallocHashCurrentCollisions++;
+ if (IMallocHashCurrentCollisions > IMallocHashMaxCollisions)
+ IMallocHashMaxCollisions = IMallocHashCurrentCollisions;
+
+ // Eintrag ist schon belegt, verkette die Einträge
+ // wenn dies oft vorkommt, sollte man entweder die Tabelle vergrößern oder eine
+ // andere Hash-Funktion wählen
+ while(pHashEntry->Next != NULL) {
+ pHashEntry = pHashEntry->Next;
+ }
+
+ pHashEntry->Next = (IMallocHashEntryType*) _calloc_dbg(sizeof(IMallocHashEntryType), 1, _CRT_BLOCK, __FILE__, __LINE__);
+ pHashEntry = pHashEntry->Next;
+
+ }
+ pHashEntry->pData = pData; // Key-Word
+ pHashEntry->nDataSize = nDataSize;
+ pHashEntry->Next = NULL;
+ // Get EIP and save it in the record
+ pHashEntry->dwEIPOffset = Context.Eip;
+ if (ReadProcessMemory(GetCurrentProcess(), (LPCVOID) Context.Eip, &(pHashEntry->pcEIPAddr), MAX_EIP_LEN_BUF, &(pHashEntry->dwEIPLen)) == 0) {
+ // Could not read memory... remove everything...
+ memset(pHashEntry->pcEIPAddr, 0, MAX_EIP_LEN_BUF);
+ pHashEntry->dwEIPLen = 0;
+ pHashEntry->dwEIPOffset = 0;
+ }
+
+ // Get ESP and save it in the record
+ pHashEntry->dwESPOffset = Context.Ebp;
+ if (ReadProcessMemory(GetCurrentProcess(), (LPCVOID) Context.Ebp, &(pHashEntry->pcESPAddr), MAX_ESP_LEN_BUF, &(pHashEntry->dwESPLen)) == 0) {
+ // Could not read memory... remove everything...
+ memset(pHashEntry->pcESPAddr, 0, MAX_ESP_LEN_BUF);
+ pHashEntry->dwESPLen = 0;
+ pHashEntry->dwESPOffset = 0;
+
+ // Check if I tried to read too much...
+ if (GetLastError() == ERROR_PARTIAL_COPY)
+ {
+ // ask how many I can read:
+ MEMORY_BASIC_INFORMATION MemBuffer;
+ DWORD dwRet = VirtualQuery((LPCVOID) Context.Ebp, &MemBuffer, sizeof(MemBuffer));
+ if (dwRet > 0)
+ {
+ // calculate the length
+ DWORD len = ((DWORD) MemBuffer.BaseAddress + MemBuffer.RegionSize) - Context.Ebp;
+ if ( (len > 0) && (len < MAX_ESP_LEN_BUF) )
+ {
+ // try to read it again (with the shorter length)
+ if (ReadProcessMemory(GetCurrentProcess(), (LPCVOID) Context.Ebp, &(pHashEntry->pcESPAddr), len, &(pHashEntry->dwESPLen)) == 0)
+ {
+ // ok, now everything goes wrong... remove it...
+ memset(pHashEntry->pcESPAddr, 0, MAX_ESP_LEN_BUF);
+ pHashEntry->dwESPLen = 0;
+ pHashEntry->dwESPOffset = 0;
+ }
+ else
+ {
+ pHashEntry->dwESPOffset = Context.Ebp;
+ }
+ }
+ } // VirtualQuery was successfully
+ } // ERROR_PARTIAL_COPY
+ }
+}
+
+// IMallocHashFind
+// Wird ALLOC_ENTRY_NOT_FOUND zurückgegeben, so wurde der Key nicht
+// gefunden, ansonsten wird ein Zeiger auf den Hash-Eintrag zurückgegeben
+// ACHTUNG: In einem preemptiven Tasking-System kann hier nicht
+// garantiert werden, ob der Zeiger noch gültig ist, wenn er
+// zurückgegeben wird, da er von einem anderen Thread schon
+// freigegeben sein könnte.
+// Die synchronisation muß eine Ebene höher erfolgen
+static IMallocHashEntryType *IMallocHashFind(void *pData) {
+ ULONG HashIdx;
+ IMallocHashEntryType *pHashEntry;
+
+ // ermittle den Hash-Wert
+ HashIdx = IMallocHashFunction(pData);
+
+ // Eintrag darf nicht größer als die Hash-Tabelle sein
+ _ASSERTE(HashIdx < ALLOC_HASH_ENTRIES);
+
+ pHashEntry = &IMallocHashTable[HashIdx];
+ while(pHashEntry != NULL) {
+ if (pHashEntry->pData == pData) {
+ return pHashEntry;
+ }
+ pHashEntry = pHashEntry->Next;
+ }
+
+ // wenn hier angelangt, dann wurde der Eintrag nicht gefunden!
+ return (IMallocHashEntryType*) ALLOC_ENTRY_NOT_FOUND;
+} // AllocHashFind
+
+// IMallocHashRemove
+// Return: FALSE (0) : Key wurde gefunden und entfernt/markiert
+// TRUE (!=0): Key wurde nicht gefunden!
+BOOL IMallocHashRemove(void *pData) {
+ ULONG HashIdx;
+ IMallocHashEntryType *pHashEntry, *pHashEntryLast;
+
+ // ermittle den Hash-Wert
+ HashIdx = IMallocHashFunction(pData);
+
+ // Eintrag darf nicht größer als die Hash-Tabelle sein
+ _ASSERTE(HashIdx < ALLOC_HASH_ENTRIES);
+
+ pHashEntryLast = NULL;
+ pHashEntry = &IMallocHashTable[HashIdx];
+ while(pHashEntry != NULL) {
+ if (pHashEntry->pData == pData) {
+#ifdef HASH_ENTRY_REMOVE_AT_FREE
+ IMallocHashFreed++;
+ IMallocHashCurrentCount--;
+ // gebe den Speicher frei
+ if (pHashEntryLast == NULL) {
+ // Es ist ein Eintrag direkt in der Tabelle
+ if (pHashEntry->Next == NULL) {
+ // Es ist der letze Eintrag lösche also die Tabelle
+ memset(&IMallocHashTable[HashIdx], 0, sizeof(IMallocHashTable[HashIdx]));
+ }
+ else {
+ // Es sind noch Einträge verkettet, überschreibe einfach den nicht mehr gebrauchten...
+ IMallocHashEntryType *pTmp = pHashEntry->Next;
+ *pHashEntry = *(pHashEntry->Next);
+ _free_dbg(pTmp, _CRT_BLOCK);
+ }
+ return TRUE;
+ }
+ else {
+ // ich bin in einem dynamischen Bereich
+ // dies war eine kollisions, zähle also wieder zurück:
+ IMallocHashCurrentCollisions--;
+ pHashEntryLast->Next = pHashEntry->Next;
+ _free_dbg(pHashEntry, _CRT_BLOCK);
+ return TRUE;
+ }
+#else
+ // erhöhe nur den Removed counter und behalte das Object im Speicher
+ pHashEntry->cRemovedFlag++;
+ return TRUE; // erfolgreich
+#endif
+ }
+ pHashEntryLast = pHashEntry;
+ pHashEntry = pHashEntry->Next;
+ }
+
+ // wenn hier angelangt, dann wurde der Eintrag nicht gefunden!
+ return FALSE;
+}
+
+
+
+// Callback-Funtion for StackWalk für meine CallStack-Ausgabe aus der Hash-Tabelle
+static BOOL __stdcall ReadProcMemoryFromIMallocHash(HANDLE pData, DWORD64 lpBaseAddress, PVOID lpBuffer, DWORD nSize, PDWORD lpNumberOfBytesRead) {
+ // Versuche die hRequestID zu finden
+ IMallocHashEntryType *pHashEntry;
+ *lpNumberOfBytesRead = 0;
+
+ pHashEntry = IMallocHashFind((PVOID) pData);
+ if (pHashEntry == (IMallocHashEntryType*) ALLOC_ENTRY_NOT_FOUND) {
+ // nicht gefunden, somit kann ich den Speicher nicht lesen
+ *lpNumberOfBytesRead = 0;
+ return FALSE;
+ }
+ if ( ((DWORD) lpBaseAddress >= pHashEntry->dwESPOffset) && ((DWORD) lpBaseAddress <= (pHashEntry->dwESPOffset+pHashEntry->dwESPLen)) ) {
+ // Speicher liegt im ESP:
+ // Errechne den Offset
+ DWORD dwOffset = (DWORD) lpBaseAddress - pHashEntry->dwESPOffset;
+ DWORD dwSize = __min(nSize, MAX_ESP_LEN_BUF-dwOffset);
+ memcpy(lpBuffer, &(pHashEntry->pcESPAddr[dwOffset]), dwSize);
+ *lpNumberOfBytesRead = dwSize;
+ if (dwSize != nSize)
+ return FALSE;
+ }
+
+ if ( ((DWORD) lpBaseAddress >= pHashEntry->dwEIPOffset) && ((DWORD) lpBaseAddress <= (pHashEntry->dwEIPOffset+pHashEntry->dwEIPLen)) ) {
+ // Speicher liegt im EIP:
+ // Errechne den Offset
+ DWORD dwOffset = (DWORD) lpBaseAddress - pHashEntry->dwEIPOffset;
+ DWORD dwSize = __min(nSize, MAX_ESP_LEN_BUF-dwOffset);
+ memcpy(lpBuffer, &(pHashEntry->pcEIPAddr[dwOffset]), dwSize);
+ *lpNumberOfBytesRead = dwSize;
+ if (dwSize != nSize)
+ return FALSE;
+ }
+
+ if (*lpNumberOfBytesRead == 0) // Der Speicher konnte nicht gefunden werden
+ return FALSE;
+
+ return TRUE;
+}
+// AllocHashOutLeaks
+// Gibt allen Speicher aus, der noch nicht wieder freigegeben wurde
+// Returns the number of bytes, that are not freed (leaks)
+ULONG IMallocHashOutLeaks(FILE *fFile) {
+ ULONG ulTemp;
+ IMallocHashEntryType *pHashEntry;
+ ULONG ulCount = 0;
+ ULONG ulLeaksByte = 0;
+
+ // Gehe jeden Eintrag durch und gebe ihn aus
+ for(ulTemp = 0; ulTemp < ALLOC_HASH_ENTRIES; ulTemp++) {
+ pHashEntry = &IMallocHashTable[ulTemp];
+ if (pHashEntry->pData != 0) {
+ while(pHashEntry != NULL) {
+ // gebe die Zeile aus
+ if ( (pHashEntry->cRemovedFlag <= 0) || (pHashEntry->cRemovedFlag > 1) ) {
+ ulCount++;
+ if (g_CallstackOutputType == ACOutput_XML)
+ _ftprintf(fFile, _T("<LEAK requestID=\"%u\" size=\"%u\">\n"), pHashEntry->pData, pHashEntry->nDataSize);
+ else
+ _ftprintf(fFile, _T("Pointer (RequestID): %12i, Removed: %i, Size: %12i\n"), pHashEntry->pData, pHashEntry->cRemovedFlag, pHashEntry->nDataSize);
+ CONTEXT c;
+ memset( &c, '\0', sizeof c );
+ c.Eip = pHashEntry->dwEIPOffset;
+ c.Ebp = pHashEntry->dwESPOffset;
+ ShowStackRM( NULL, c, fFile, &ReadProcMemoryFromIMallocHash, (HANDLE) pHashEntry->pData);
+ // Zähle zusammen wieviel Byte noch nicht freigegeben wurden
+ if (pHashEntry->nDataSize > 0)
+ ulLeaksByte += pHashEntry->nDataSize;
+ else
+ ulLeaksByte++; // Wenn zwar Speicher allokiert wurde, dieser aber 0 Bytes lang war, so reserviere für diesen zumindest 1 Byte
+
+ if (g_CallstackOutputType == ACOutput_XML)
+ _ftprintf(fFile, _T("</LEAK>\n")); // terminate the xml-node
+ }
+ pHashEntry = pHashEntry->Next;
+ }
+ }
+ }
+ if (g_CallstackOutputType != ACOutput_XML)
+ _ftprintf(fFile, _T("\n**** Number of leaks: %i\n"), ulCount);
+ return ulLeaksByte;
+} // AllocHashOutLeaks
+#endif
+
+
+static void AllocHashInit(void) {
+
+ memset(AllocHashTable, 0, sizeof(AllocHashTable));
+ AllocHashEntries = 0;
+ AllocHashCollisions = 0;
+ AllocHashFreed = 0;
+ AllocHashCurrentCount = 0;
+ AllocHashMaxUsed = 0;
+
+ AllocHashMaxCollisions = 0;
+ AllocHashCurrentCollisions = 0;
+
+#ifdef WITH_IMALLOC_SPY
+ memset(IMallocHashTable, 0, sizeof(IMallocHashTable));
+ IMallocHashEntries = 0;
+ IMallocHashCollisions = 0;
+ IMallocHashFreed = 0;
+ IMallocHashCurrentCount = 0;
+ IMallocHashMaxUsed = 0;
+
+ IMallocHashMaxCollisions = 0;
+ IMallocHashCurrentCollisions = 0;
+#endif
+ return;
+} // AllocHashInit
+
+
+// AllocHashDeinit
+// Returns the number of bytes, that are not freed (leaks)
+static ULONG AllocHashDeinit(void) {
+ ULONG ulRet = 0;
+ bool bAppend = g_CallstackOutputType != ACOutput_XML;
+ AllocCheckFileOpen(false);//bAppend); // open global log-file
+
+ if (g_CallstackOutputType == ACOutput_XML)
+ {
+ _ftprintf(g_fFile, _T("<MEMREPORT "));
+ WriteDateTime(g_fFile, TRUE);
+ _ftprintf(g_fFile, _T(">\n"));
+ }
+ else
+ {
+ _ftprintf(g_fFile, _T("\n##### Memory Report ########################################\n"));
+ WriteDateTime(g_fFile);
+ _ftprintf(g_fFile, _T("\n"));
+ }
+
+#ifndef HASH_ENTRY_REMOVE_AT_FREE
+ // output the used memory
+ if (g_CallstackOutputType != ACOutput_XML)
+ _ftprintf(g_fFile, _T("##### Memory used: #########################################\n"));
+ AllocHashOut(g_fFile);
+#endif
+
+ // output the Memoty leaks
+ if (g_CallstackOutputType != ACOutput_XML)
+ _ftprintf(g_fFile, _T("\n##### Leaks: ###############################################\n"));
+ ulRet = AllocHashOutLeaks(g_fFile);
+
+ if (g_CallstackOutputType == ACOutput_Advanced)
+ {
+ // output some statistics from the hash-table
+ _ftprintf(g_fFile, _T("***** Hash-Table statistics:\n"));
+ _ftprintf(g_fFile, _T(" Table-Size: %i\n"), ALLOC_HASH_ENTRIES);
+ _ftprintf(g_fFile, _T(" Inserts: %i\n"), AllocHashEntries);
+ _ftprintf(g_fFile, _T(" Freed: %i\n"), AllocHashFreed);
+ _ftprintf(g_fFile, _T(" Sum Collisions: %i\n"), AllocHashCollisions);
+ _ftprintf(g_fFile, _T("\n"));
+ _ftprintf(g_fFile, _T(" Max used: %i\n"), AllocHashMaxUsed);
+ _ftprintf(g_fFile, _T(" Max Collisions: %i\n"), AllocHashMaxCollisions);
+ }
+
+ // Free Hash-Table
+ ULONG ulTemp;
+ AllocHashEntryType *pHashEntry, *pHashEntryOld;
+
+ // Now, free my own memory
+ for(ulTemp = 0; ulTemp < ALLOC_HASH_ENTRIES; ulTemp++) {
+ pHashEntry = &AllocHashTable[ulTemp];
+ while(pHashEntry != NULL) {
+ pHashEntryOld = pHashEntry;
+ pHashEntry = pHashEntry->Next;
+ if (pHashEntryOld != &AllocHashTable[ulTemp]) {
+ // now free the dynamically allocated memory
+ free(pHashEntryOld);
+ }
+ } // while
+ } // for
+ // empty the hash-table
+ memset(AllocHashTable, 0, sizeof(AllocHashTable));
+
+#ifdef WITH_IMALLOC_SPY
+ // output the Memoty leaks
+ if (g_CallstackOutputType != ACOutput_XML)
+ _ftprintf(g_fFile, _T("\n##### COM-Leaks: ###############################################\n"));
+ ulRet = IMallocHashOutLeaks(g_fFile);
+
+ if (g_CallstackOutputType == ACOutput_Advanced)
+ {
+ // output some statistics from the hash-table
+ _ftprintf(g_fFile, _T("***** Hash-Table statistics:\n"));
+ _ftprintf(g_fFile, _T(" Table-Size: %i\n"), ALLOC_HASH_ENTRIES);
+ _ftprintf(g_fFile, _T(" Inserts: %i\n"), IMallocHashEntries);
+ _ftprintf(g_fFile, _T(" Freed: %i\n"), IMallocHashFreed);
+ _ftprintf(g_fFile, _T(" Sum Collisions: %i\n"), IMallocHashCollisions);
+ _ftprintf(g_fFile, _T("\n"));
+ _ftprintf(g_fFile, _T(" Max used: %i\n"), IMallocHashMaxUsed);
+ _ftprintf(g_fFile, _T(" Max Collisions: %i\n"), IMallocHashMaxCollisions);
+ }
+
+ // Free Hash-Table
+ //ULONG ulTemp;
+ IMallocHashEntryType *pIMHashEntry, *pIMHashEntryOld;
+
+ // Gehe jeden Eintrag durch und gebe ihn frei
+ for(ulTemp = 0; ulTemp < ALLOC_HASH_ENTRIES; ulTemp++) {
+ pIMHashEntry = &IMallocHashTable[ulTemp];
+ while(pHashEntry != NULL) {
+ pIMHashEntryOld = pIMHashEntry;
+ pIMHashEntry = pIMHashEntry->Next;
+ if (pIMHashEntryOld != &IMallocHashTable[ulTemp]) {
+ // es ist dynamischer Speicher, gebe ihn also frei:
+ _free_dbg(pIMHashEntryOld, _CRT_BLOCK);
+ }
+ } // while
+ } // for
+ // Lösche die gesamte Hash-Tabelle
+ memset(IMallocHashTable, 0, sizeof(IMallocHashTable));
+#endif
+
+
+ if (g_CallstackOutputType == ACOutput_XML)
+ _ftprintf(g_fFile, _T("</MEMREPORT>\n"));
+
+ return ulRet;
+} // AllocHashDeinit
+
+// AllocHashFunction
+// The has-function (very simple)
+static inline ULONG AllocHashFunction(long lRequestID) {
+ // I couldn´t find any better and faster
+ return lRequestID % ALLOC_HASH_ENTRIES;
+} // AllocHashFunction
+
+// AllocHashInsert
+// lRequestID: Key-Word (RequestID from AllocHook)
+// pContext: Context-Record, for retrieving Callstack (EIP and EBP is only needed)
+// nDataSize: How many bytes
+static void AllocHashInsert(long lRequestID, CONTEXT &Context, size_t nDataSize) {
+ ULONG HashIdx;
+ AllocHashEntryType *pHashEntry;
+
+ // change statistical data
+ AllocHashEntries++;
+ AllocHashCurrentCount++;
+ if (AllocHashCurrentCount > AllocHashMaxUsed)
+ AllocHashMaxUsed = AllocHashCurrentCount;
+
+ // generate hash-value
+ HashIdx = AllocHashFunction(lRequestID);
+
+ pHashEntry = &AllocHashTable[HashIdx];
+ if (pHashEntry->lRequestID == 0) {
+ // Entry is empty...
+ }
+ else {
+ // Entry is not empy! make a list of entries for this hash value...
+ // change statistical data
+ // if this happens often, you should increase the hah size or change the heash-function;
+ // to fasten the allocation time
+ AllocHashCollisions++;
+ AllocHashCurrentCollisions++;
+ if (AllocHashCurrentCollisions > AllocHashMaxCollisions)
+ AllocHashMaxCollisions = AllocHashCurrentCollisions;
+
+ while(pHashEntry->Next != NULL) {
+ pHashEntry = pHashEntry->Next;
+ }
+
+ pHashEntry->Next = (AllocHashEntryType*) calloc(sizeof(AllocHashEntryType), 1);
+ pHashEntry = pHashEntry->Next;
+
+ }
+ pHashEntry->lRequestID = lRequestID; // Key-Word
+ pHashEntry->nDataSize = nDataSize;
+ pHashEntry->Next = NULL;
+ // Get EIP and save it in the record
+ pHashEntry->dwEIPOffset = Context.Eip;
+ if (ReadProcessMemory(GetCurrentProcess(), (LPCVOID) Context.Eip, &(pHashEntry->pcEIPAddr), MAX_EIP_LEN_BUF, &(pHashEntry->dwEIPLen)) == 0) {
+ // Could not read memory... remove everything...
+ memset(pHashEntry->pcEIPAddr, 0, MAX_EIP_LEN_BUF);
+ pHashEntry->dwEIPLen = 0;
+ pHashEntry->dwEIPOffset = 0;
+ }
+
+ // Get ESP and save it in the record
+ pHashEntry->dwESPOffset = Context.Ebp;
+ if (ReadProcessMemory(GetCurrentProcess(), (LPCVOID) Context.Ebp, &(pHashEntry->pcESPAddr), MAX_ESP_LEN_BUF, &(pHashEntry->dwESPLen)) == 0) {
+ // Could not read memory... remove everything...
+ memset(pHashEntry->pcESPAddr, 0, MAX_ESP_LEN_BUF);
+ pHashEntry->dwESPLen = 0;
+ pHashEntry->dwESPOffset = 0;
+
+ // Check if I tried to read too much...
+ if (GetLastError() == ERROR_PARTIAL_COPY)
+ {
+ // ask how many I can read:
+ MEMORY_BASIC_INFORMATION MemBuffer;
+ DWORD dwRet = VirtualQuery((LPCVOID) Context.Ebp, &MemBuffer, sizeof(MemBuffer));
+ if (dwRet > 0)
+ {
+ // calculate the length
+ DWORD len = ((DWORD) MemBuffer.BaseAddress + MemBuffer.RegionSize) - Context.Ebp;
+ if ( (len > 0) && (len < MAX_ESP_LEN_BUF) )
+ {
+ // try to read it again (with the shorter length)
+ if (ReadProcessMemory(GetCurrentProcess(), (LPCVOID) Context.Ebp, &(pHashEntry->pcESPAddr), len, &(pHashEntry->dwESPLen)) == 0)
+ {
+ // ok, now everything goes wrong... remove it...
+ memset(pHashEntry->pcESPAddr, 0, MAX_ESP_LEN_BUF);
+ pHashEntry->dwESPLen = 0;
+ pHashEntry->dwESPOffset = 0;
+ }
+ else
+ {
+ pHashEntry->dwESPOffset = Context.Ebp;
+ }
+ }
+ } // VirtualQuery was successfully
+ } // ERROR_PARTIAL_COPY
+ }
+}
+
+// AllocHashFind
+// If ALLOC_ENTRY_NOT_FOUND is returned, the Key was not found!
+// If the Key was found, a pointer to the entry is returned
+static AllocHashEntryType *AllocHashFind(long lRequestID) {
+ ULONG HashIdx;
+ AllocHashEntryType *pHashEntry;
+
+ // get the Hash-Value
+ HashIdx = AllocHashFunction(lRequestID);
+
+ // Just do some simple checks:
+ _ASSERTE(HashIdx < ALLOC_HASH_ENTRIES);
+
+ pHashEntry = &AllocHashTable[HashIdx];
+ while(pHashEntry != NULL) {
+ if (pHashEntry->lRequestID == lRequestID) {
+ return pHashEntry;
+ }
+ pHashEntry = pHashEntry->Next;
+ }
+
+ // entry was not found!
+ return (AllocHashEntryType*) ALLOC_ENTRY_NOT_FOUND;
+} // AllocHashFind
+
+// AllocHashRemove
+// Return: FALSE (0) : Key was found and removed/marked
+// TRUE (!=0): Key was not found
+static BOOL AllocHashRemove(long lRequestID) {
+ ULONG HashIdx;
+ AllocHashEntryType *pHashEntry, *pHashEntryLast;
+
+ // get the Hash-Value
+ HashIdx = AllocHashFunction(lRequestID);
+
+ // Just do some simple checks:
+ _ASSERTE(HashIdx < ALLOC_HASH_ENTRIES);
+
+ pHashEntryLast = NULL;
+ pHashEntry = &AllocHashTable[HashIdx];
+ while(pHashEntry != NULL) {
+ if (pHashEntry->lRequestID == lRequestID) {
+#ifdef HASH_ENTRY_REMOVE_AT_FREE
+ AllocHashFreed++;
+ AllocHashCurrentCount--;
+ // release my memory
+ if (pHashEntryLast == NULL) {
+ // It is an entry in the table, so do not release this memory
+ if (pHashEntry->Next == NULL) {
+ // It was the last entry, so empty the table entry
+ memset(&AllocHashTable[HashIdx], 0, sizeof(AllocHashTable[HashIdx]));
+ }
+ else {
+ // There are some more entries, so shorten the list
+ AllocHashEntryType *pTmp = pHashEntry->Next;
+ *pHashEntry = *(pHashEntry->Next);
+ free(pTmp);
+ }
+ return TRUE;
+ }
+ else {
+ // now, I am in an dynamic allocated entry
+ // it was a collision, so decrease the current collision count
+ AllocHashCurrentCollisions--;
+ pHashEntryLast->Next = pHashEntry->Next;
+ free(pHashEntry);
+ return TRUE;
+ }
+#else
+ // increase the Remove-Count and let the objet stay in memory
+ pHashEntry->cRemovedFlag++;
+ return TRUE;
+#endif
+ }
+ pHashEntryLast = pHashEntry;
+ pHashEntry = pHashEntry->Next;
+ }
+
+ // if we are here, we could not find the RequestID
+ return FALSE;
+}
+
+// ReadProcMemoryFromHash
+// Callback-Funtion for StackWalk for my own CallStack from the Hash-Table-Entries
+static BOOL __stdcall ReadProcMemoryFromHash(HANDLE hRequestID, DWORD64 lpBaseAddress, PVOID lpBuffer, DWORD nSize, PDWORD lpNumberOfBytesRead) {
+ // Try to find the RequestID
+ AllocHashEntryType *pHashEntry;
+ *lpNumberOfBytesRead = 0;
+
+ pHashEntry = AllocHashFind((LONG) hRequestID);
+ if (pHashEntry == (AllocHashEntryType*) ALLOC_ENTRY_NOT_FOUND) {
+ // Not found, so I cannot return any memory
+ *lpNumberOfBytesRead = 0;
+ return FALSE;
+ }
+ if ( ((DWORD) lpBaseAddress >= pHashEntry->dwESPOffset) && ((DWORD) lpBaseAddress <= (pHashEntry->dwESPOffset+pHashEntry->dwESPLen)) ) {
+ // Memory is located in ESP:
+ // Calculate the offset
+ DWORD dwOffset = (DWORD) lpBaseAddress - pHashEntry->dwESPOffset;
+ DWORD dwSize = __min(nSize, MAX_ESP_LEN_BUF-dwOffset);
+ memcpy(lpBuffer, &(pHashEntry->pcESPAddr[dwOffset]), dwSize);
+ *lpNumberOfBytesRead = dwSize;
+ if (dwSize != nSize)
+ return FALSE;
+ }
+
+ if ( ((DWORD) lpBaseAddress >= pHashEntry->dwEIPOffset) && ((DWORD) lpBaseAddress <= (pHashEntry->dwEIPOffset+pHashEntry->dwEIPLen)) ) {
+ // Memory is located in EIP:
+ // Calculate the offset
+ DWORD dwOffset = (DWORD) lpBaseAddress - pHashEntry->dwEIPOffset;
+ DWORD dwSize = __min(nSize, MAX_ESP_LEN_BUF-dwOffset);
+ memcpy(lpBuffer, &(pHashEntry->pcEIPAddr[dwOffset]), dwSize);
+ *lpNumberOfBytesRead = dwSize;
+ if (dwSize != nSize)
+ return FALSE;
+ }
+
+ if (*lpNumberOfBytesRead == 0) // Memory could not be found
+ return FALSE;
+
+ return TRUE;
+}
+
+// AllocHashOutLeaks
+// Write all Memory (with callstack) which was not freed yet
+// Returns the number of bytes, that are not freed (leaks)
+ULONG AllocHashOutLeaks(FILE *fFile) {
+ ULONG ulTemp;
+ AllocHashEntryType *pHashEntry;
+ ULONG ulCount = 0;
+ ULONG ulLeaksByte = 0;
+
+ // Move throu every entry
+ for(ulTemp = 0; ulTemp < ALLOC_HASH_ENTRIES; ulTemp++) {
+ pHashEntry = &AllocHashTable[ulTemp];
+ if (pHashEntry->lRequestID != 0) {
+ while(pHashEntry != NULL) {
+ if ( (pHashEntry->cRemovedFlag <= 0) || (pHashEntry->cRemovedFlag > 1) ) {
+ ulCount++;
+ if (g_CallstackOutputType == ACOutput_XML)
+ _ftprintf(fFile, _T("<LEAK requestID=\"%u\" size=\"%u\">\n"), pHashEntry->lRequestID, pHashEntry->nDataSize);
+ else
+ _ftprintf(fFile, _T("RequestID: %12i, Removed: %i, Size: %12i\n"), pHashEntry->lRequestID, pHashEntry->cRemovedFlag, pHashEntry->nDataSize);
+ CONTEXT c;
+ memset( &c, '\0', sizeof c );
+ c.Eip = pHashEntry->dwEIPOffset;
+ c.Ebp = pHashEntry->dwESPOffset;
+ ShowStackRM( NULL, c, fFile, &ReadProcMemoryFromHash, (HANDLE) pHashEntry->lRequestID);
+ // Count the number of leaky bytes
+ if (pHashEntry->nDataSize > 0)
+ ulLeaksByte += pHashEntry->nDataSize;
+ else
+ ulLeaksByte++; // If memory was allocated with zero bytes, then just increase the counter 1
+
+ if (g_CallstackOutputType == ACOutput_XML)
+ _ftprintf(fFile, _T("</LEAK>\n")); // terminate the xml-node
+ }
+ pHashEntry = pHashEntry->Next;
+ }
+ }
+ }
+ if (g_CallstackOutputType != ACOutput_XML)
+ _ftprintf(fFile, _T("\n**** Number of leaks: %i\n"), ulCount);
+ return ulLeaksByte;
+} // AllocHashOutLeaks
+
+// Write all used memory to a file
+void AllocHashOut(FILE *fFile) {
+ ULONG ulTemp;
+ AllocHashEntryType *pHashEntry;
+
+ for(ulTemp = 0; ulTemp < ALLOC_HASH_ENTRIES; ulTemp++) {
+ pHashEntry = &AllocHashTable[ulTemp];
+ if (pHashEntry->lRequestID != 0) {
+ while(pHashEntry != NULL) {
+ if (g_CallstackOutputType == ACOutput_XML)
+ _ftprintf(fFile, _T("<MEMUSED requestID=\"%u\" size=\"%u\"\n"), pHashEntry->lRequestID, pHashEntry->nDataSize);
+ else
+ _ftprintf(fFile, _T("RequestID: %12i, Removed: %i, Size: %12i\n"), pHashEntry->lRequestID, pHashEntry->cRemovedFlag, pHashEntry->nDataSize);
+ pHashEntry = pHashEntry->Next;
+ }
+ }
+ }
+} // AllocHashOut
+
+/*******************************************************************************
+ * Ende der Hash-Tabelle
+ *******************************************************************************/
+
+
+// The follwoing is copied from dbgint.h:
+// <CRT_INTERNALS>
+/*
+ * For diagnostic purpose, blocks are allocated with extra information and
+ * stored in a doubly-linked list. This makes all blocks registered with
+ * how big they are, when they were allocated, and what they are used for.
+ */
+
+#define nNoMansLandSize 4
+
+typedef struct _CrtMemBlockHeader
+{
+ struct _CrtMemBlockHeader * pBlockHeaderNext;
+ struct _CrtMemBlockHeader * pBlockHeaderPrev;
+ char * szFileName;
+ int nLine;
+#ifdef _WIN64
+ /* These items are reversed on Win64 to eliminate gaps in the struct
+ * and ensure that sizeof(struct)%16 == 0, so 16-byte alignment is
+ * maintained in the debug heap.
+ */
+ int nBlockUse;
+ size_t nDataSize;
+#else /* _WIN64 */
+ size_t nDataSize;
+ int nBlockUse;
+#endif /* _WIN64 */
+ long lRequest;
+ unsigned char gap[nNoMansLandSize];
+ /* followed by:
+ * unsigned char data[nDataSize];
+ * unsigned char anotherGap[nNoMansLandSize];
+ */
+} _CrtMemBlockHeader;
+#define pbData(pblock) ((unsigned char *)((_CrtMemBlockHeader *)pblock + 1))
+#define pHdr(pbData) (((_CrtMemBlockHeader *)pbData)-1)
+
+// </CRT_INTERNALS>
+
+
+
+
+// Global data:
+static BOOL g_bInitialized = FALSE;
+static HINSTANCE g_hImagehlpDll = NULL;
+
+static DWORD g_dwShowCount = 0; // increase at every ShowStack-Call
+static CRITICAL_SECTION g_csFileOpenClose = {0};
+
+// Is used for syncronising call to MyAllocHook (to prevent reentrant calls)
+static LONG g_lMallocCalled = 0;
+
+static _CRT_ALLOC_HOOK pfnOldCrtAllocHook = NULL;
+
+// Deaktivate AllocHook, by increasing the Syncronisation-Counter
+//static void DeactivateMallocStackwalker(void) {
+// InterlockedIncrement(&g_lMallocCalled);
+//}
+
+
+// MyAllocHook is Single-Threaded, that means the the calls are serialized in the calling function!
+// Special case for VC 5
+#if _MSC_VER <= 1100
+static int MyAllocHook(int nAllocType, void *pvData,
+ size_t nSize, int nBlockUse, long lRequest,
+ const char * szFileName, int nLine ) {
+#else
+static int MyAllocHook(int nAllocType, void *pvData,
+ size_t nSize, int nBlockUse, long lRequest,
+ const unsigned char * szFileName, int nLine ) {
+#endif
+ static TCHAR *operation[] = { _T(""), _T("ALLOCATIONG"), _T("RE-ALLOCATING"), _T("FREEING") };
+ static TCHAR *blockType[] = { _T("Free"), _T("Normal"), _T("CRT"), _T("Ignore"), _T("Client") };
+
+#ifdef IGNORE_CRT_ALLOC
+ if (_BLOCK_TYPE(nBlockUse) == _CRT_BLOCK) // Ignore internal C runtime library allocations
+ return TRUE;
+#endif
+ extern int _crtDbgFlag;
+ if ( ((_CRTDBG_ALLOC_MEM_DF & _crtDbgFlag) == 0) && ( (nAllocType == _HOOK_ALLOC) || (nAllocType == _HOOK_REALLOC) ) )
+ {
+ // Someone has disabled that the runtime should log this allocation
+ // so we do not log this allocation
+ if (pfnOldCrtAllocHook != NULL)
+ pfnOldCrtAllocHook(nAllocType, pvData, nSize, nBlockUse, lRequest, szFileName, nLine);
+ return TRUE;
+ }
+
+ // Prevent from reentrat calls
+ if (InterlockedIncrement(&g_lMallocCalled) > 1) { // I was already called
+ InterlockedDecrement(&g_lMallocCalled);
+ // call the previous alloc hook
+ if (pfnOldCrtAllocHook != NULL)
+ pfnOldCrtAllocHook(nAllocType, pvData, nSize, nBlockUse, lRequest, szFileName, nLine);
+ return TRUE;
+ }
+
+ if (g_ulShowStackAtAlloc > 0) {
+ AllocCheckFileOpen(); // Open logfile
+ }
+
+ _ASSERT( (nAllocType == _HOOK_ALLOC) || (nAllocType == _HOOK_REALLOC) || (nAllocType == _HOOK_FREE) );
+ _ASSERT( ( _BLOCK_TYPE(nBlockUse) >= 0 ) && ( _BLOCK_TYPE(nBlockUse) < 5 ) );
+
+ if (nAllocType == _HOOK_FREE) { // freeing
+ // Try to get the header information
+ if (_CrtIsValidHeapPointer(pvData)) { // it is a valid Heap-Pointer
+ // get the ID
+ _CrtMemBlockHeader *pHead;
+ // get a pointer to memory block header
+ pHead = pHdr(pvData);
+ nSize = pHead->nDataSize;
+ lRequest = pHead->lRequest; // This is the ID!
+
+ if (pHead->nBlockUse == _IGNORE_BLOCK)
+ {
+ InterlockedDecrement(&g_lMallocCalled);
+ if (pfnOldCrtAllocHook != NULL)
+ pfnOldCrtAllocHook(nAllocType, pvData, nSize, nBlockUse, lRequest, szFileName, nLine);
+ return TRUE;
+ }
+ }
+ }
+
+ if (g_ulShowStackAtAlloc > 0) {
+ _ftprintf( g_fFile, _T("##### Memory operation: %s a %d-byte '%s' block (# %ld)"),
+ operation[nAllocType], nSize, blockType[_BLOCK_TYPE(nBlockUse)], lRequest );
+ if ( pvData != NULL )
+ _ftprintf( g_fFile, _T(" at 0x%X"), pvData );
+ _ftprintf(g_fFile, _T("\n"));
+ }
+
+ if (nAllocType == _HOOK_FREE) { // freeing:
+ if (lRequest != 0) { // RequestID was found
+ BOOL bRet;
+ // Try to find the RequestID in the Hash-Table, mark it that it was freed
+ bRet = AllocHashRemove(lRequest);
+ if(g_ulShowStackAtAlloc > 0) {
+ if (bRet == FALSE) {
+ // RequestID not found!
+ _ftprintf(g_fFile, _T("###### RequestID not found in hash table for FREEING (%i)!\n"), lRequest);
+ }
+ } // g_ulShowStackAtAlloc > 0
+ }
+ else {
+ if(g_ulShowStackAtAlloc > 0) {
+ // No valid RequestID found, display error
+ _ftprintf(g_fFile, _T("###### No valid RequestID for FREEING! (0x%X)\n"), pvData);
+
+ }
+ }
+ } // freeing
+
+ if (nAllocType == _HOOK_REALLOC) { // re-allocating
+ // Try to get the header information
+ if (_CrtIsValidHeapPointer(pvData)) { // it is a valid Heap-Pointer
+ BOOL bRet;
+ LONG lReallocRequest;
+ // get the ID
+ _CrtMemBlockHeader *pHead;
+ // get a pointer to memory block header
+ pHead = pHdr(pvData);
+ // Try to find the RequestID in the Hash-Table, mark it that it was freed
+ lReallocRequest = pHead->lRequest;
+ bRet = AllocHashRemove(lReallocRequest);
+ if (g_ulShowStackAtAlloc > 0) {
+ if (bRet == FALSE) {
+ // RequestID not found!
+ _ftprintf(g_fFile, _T("###### RequestID not found in hash table for RE-ALLOCATING (%i)!\n"), lReallocRequest);
+ }
+ else {
+ _ftprintf(g_fFile, _T("##### Implicit freeing because of re-allocation (# old: %ld, new: %ld)\n"), lReallocRequest, lRequest);
+ }
+ } // g_ulShowStackAtAlloc > 0
+ } // ValidHeapPointer
+ } // re-allocating
+
+ if ( (g_ulShowStackAtAlloc < 3) && (nAllocType == _HOOK_FREE) ) {
+ InterlockedDecrement(&g_lMallocCalled);
+ // call the previous alloc hook
+ if (pfnOldCrtAllocHook != NULL)
+ pfnOldCrtAllocHook(nAllocType, pvData, nSize, nBlockUse, lRequest, szFileName, nLine);
+ return TRUE;
+ }
+
+ HANDLE hThread;
+ if (DuplicateHandle( GetCurrentProcess(), GetCurrentThread(),
+ GetCurrentProcess(), &hThread, 0, false, DUPLICATE_SAME_ACCESS ) == 0) {
+ // Something was wrong...
+ _ftprintf(g_fFile, _T("###### Could not call 'DuplicateHandle' successfully\n"));
+ InterlockedDecrement(&g_lMallocCalled);
+ // call the previous alloc hook
+ if (pfnOldCrtAllocHook != NULL)
+ pfnOldCrtAllocHook(nAllocType, pvData, nSize, nBlockUse, lRequest, szFileName, nLine);
+ return TRUE;
+ }
+
+ CONTEXT c;
+ memset( &c, '\0', sizeof c );
+ c.ContextFlags = CONTEXT_FULL;
+
+ // Get the context of this thread
+#if 0
+ // init CONTEXT record so we know where to start the stackwalk
+ if ( MyGetCurrentThreadContext( hThread, &c ) == 0) {
+ if(g_ulShowStackAtAlloc > 1) {
+ _ftprintf(g_fFile, _T("###### Could not call 'GetThreadContext' successfully\n"));
+ }
+ InterlockedDecrement(&g_lMallocCalled);
+ // call the previous alloc hook
+ if (pfnOldCrtAllocHook != NULL)
+ pfnOldCrtAllocHook(nAllocType, pvData, nSize, nBlockUse, lRequest, szFileName, nLine);
+ CloseHandle(hThread);
+ return TRUE; // could not get context
+ }
+#else
+ __asm
+ {
+ call x
+ x: pop eax
+ mov c.Eip, eax
+ mov c.Ebp, ebp
+ }
+#endif
+
+ if(g_ulShowStackAtAlloc > 1) {
+ if(g_ulShowStackAtAlloc > 2) {
+ // output the callstack
+ ShowStack( hThread, c, g_fFile);
+ }
+ else {
+ // Output only (re)allocs
+ if (nAllocType != _HOOK_FREE) {
+ ShowStack( hThread, c, g_fFile);
+ }
+ }
+ } // g_ulShowStackAtAlloc > 1
+ CloseHandle( hThread );
+
+ // Only isert in the Hash-Table if it is not a "freeing"
+ if (nAllocType != _HOOK_FREE) {
+ if(lRequest != 0) // Always a valid RequestID should be provided (see comments in the header)
+ AllocHashInsert(lRequest, c, nSize);
+ }
+
+ InterlockedDecrement(&g_lMallocCalled);
+ // call the previous alloc hook
+ if (pfnOldCrtAllocHook != NULL)
+ pfnOldCrtAllocHook(nAllocType, pvData, nSize, nBlockUse, lRequest, szFileName, nLine);
+ return TRUE; // allow the memory operation to proceed
+}
+
+
+
+
+// ##########################################################################################
+// ##########################################################################################
+// ##########################################################################################
+// ##########################################################################################
+
+#define gle (GetLastError())
+#define lenof(a) (sizeof(a) / sizeof((a)[0]))
+#define MAXNAMELEN 1024 // max name length for found symbols
+#define IMGSYMLEN ( sizeof IMAGEHLP_SYMBOL64 )
+#define TTBUFLEN 8096 // for a temp buffer (2^13)
+
+
+
+// SymCleanup()
+typedef BOOL (__stdcall *tSC)( IN HANDLE hProcess );
+tSC pSC = NULL;
+
+// SymFunctionTableAccess64()
+typedef PVOID (__stdcall *tSFTA)( HANDLE hProcess, DWORD64 AddrBase );
+tSFTA pSFTA = NULL;
+
+// SymGetLineFromAddr64()
+typedef BOOL (__stdcall *tSGLFA)( IN HANDLE hProcess, IN DWORD64 dwAddr,
+ OUT PDWORD pdwDisplacement, OUT PIMAGEHLP_LINE64 Line );
+tSGLFA pSGLFA = NULL;
+
+// SymGetModuleBase64()
+typedef DWORD64 (__stdcall *tSGMB)( IN HANDLE hProcess, IN DWORD64 dwAddr );
+tSGMB pSGMB = NULL;
+
+// SymGetModuleInfo64()
+typedef BOOL (__stdcall *tSGMI)( IN HANDLE hProcess, IN DWORD64 dwAddr, OUT PIMAGEHLP_MODULE64 ModuleInfo );
+tSGMI pSGMI = NULL;
+
+// SymGetOptions()
+typedef DWORD (__stdcall *tSGO)( VOID );
+tSGO pSGO = NULL;
+
+// SymGetSymFromAddr64()
+typedef BOOL (__stdcall *tSGSFA)( IN HANDLE hProcess, IN DWORD64 dwAddr,
+ OUT PDWORD64 pdwDisplacement, OUT PIMAGEHLP_SYMBOL64 Symbol );
+tSGSFA pSGSFA = NULL;
+
+// SymInitialize()
+typedef BOOL (__stdcall *tSI)( IN HANDLE hProcess, IN PSTR UserSearchPath, IN BOOL fInvadeProcess );
+tSI pSI = NULL;
+
+// SymLoadModule64()
+typedef DWORD (__stdcall *tSLM)( IN HANDLE hProcess, IN HANDLE hFile,
+ IN PSTR ImageName, IN PSTR ModuleName, IN DWORD64 BaseOfDll, IN DWORD SizeOfDll );
+tSLM pSLM = NULL;
+
+// SymSetOptions()
+typedef DWORD (__stdcall *tSSO)( IN DWORD SymOptions );
+tSSO pSSO = NULL;
+
+// StackWalk64()
+typedef BOOL (__stdcall *tSW)(
+ DWORD MachineType,
+ HANDLE hProcess,
+ HANDLE hThread,
+ LPSTACKFRAME64 StackFrame,
+ PVOID ContextRecord,
+ PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryRoutine,
+ PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine,
+ PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine,
+ PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress );
+tSW pSW = NULL;
+
+// UnDecorateSymbolName()
+typedef DWORD (__stdcall WINAPI *tUDSN)( PCSTR DecoratedName, PSTR UnDecoratedName,
+ DWORD UndecoratedLength, DWORD Flags );
+tUDSN pUDSN = NULL;
+
+
+
+struct ModuleEntry
+{
+ std::string imageName;
+ std::string moduleName;
+ DWORD baseAddress;
+ DWORD size;
+};
+typedef std::vector< ModuleEntry > ModuleList;
+typedef ModuleList::iterator ModuleListIter;
+
+// **************************************** ToolHelp32 ************************
+#define MAX_MODULE_NAME32 255
+#define TH32CS_SNAPMODULE 0x00000008
+#pragma pack( push, 8 )
+typedef struct tagMODULEENTRY32
+{
+ DWORD dwSize;
+ DWORD th32ModuleID; // This module
+ DWORD th32ProcessID; // owning process
+ DWORD GlblcntUsage; // Global usage count on the module
+ DWORD ProccntUsage; // Module usage count in th32ProcessID's context
+ BYTE * modBaseAddr; // Base address of module in th32ProcessID's context
+ DWORD modBaseSize; // Size in bytes of module starting at modBaseAddr
+ HMODULE hModule; // The hModule of this module in th32ProcessID's context
+ char szModule[MAX_MODULE_NAME32 + 1];
+ char szExePath[MAX_PATH];
+} MODULEENTRY32;
+typedef MODULEENTRY32 * PMODULEENTRY32;
+typedef MODULEENTRY32 * LPMODULEENTRY32;
+#pragma pack( pop )
+
+
+
+static bool GetModuleListTH32(ModuleList& modules, DWORD pid, FILE *fLogFile)
+{
+ // CreateToolhelp32Snapshot()
+ typedef HANDLE (__stdcall *tCT32S)(DWORD dwFlags, DWORD th32ProcessID);
+ // Module32First()
+ typedef BOOL (__stdcall *tM32F)(HANDLE hSnapshot, LPMODULEENTRY32 lpme);
+ // Module32Next()
+ typedef BOOL (__stdcall *tM32N)(HANDLE hSnapshot, LPMODULEENTRY32 lpme);
+
+ // try both dlls...
+ const TCHAR *dllname[] = { _T("kernel32.dll"), _T("tlhelp32.dll") };
+ HINSTANCE hToolhelp;
+ tCT32S pCT32S;
+ tM32F pM32F;
+ tM32N pM32N;
+
+ HANDLE hSnap;
+ MODULEENTRY32 me;
+ me.dwSize = sizeof(me);
+ bool keepGoing;
+ ModuleEntry e;
+ int i;
+
+ for (i = 0; i<lenof(dllname); i++ )
+ {
+ hToolhelp = LoadLibrary( dllname[i] );
+ if (hToolhelp == NULL)
+ continue;
+ pCT32S = (tCT32S) GetProcAddress(hToolhelp, "CreateToolhelp32Snapshot");
+ pM32F = (tM32F) GetProcAddress(hToolhelp, "Module32First");
+ pM32N = (tM32N) GetProcAddress(hToolhelp, "Module32Next");
+ if ( pCT32S != 0 && pM32F != 0 && pM32N != 0 )
+ break; // found the functions!
+ FreeLibrary(hToolhelp);
+ hToolhelp = NULL;
+ }
+
+ if (hToolhelp == NULL)
+ return false;
+
+ hSnap = pCT32S( TH32CS_SNAPMODULE, pid );
+ if (hSnap == (HANDLE) -1)
+ return false;
+
+ keepGoing = !!pM32F( hSnap, &me );
+ while (keepGoing)
+ {
+ e.imageName = me.szExePath;
+ e.moduleName = me.szModule;
+ e.baseAddress = (DWORD) me.modBaseAddr;
+ e.size = me.modBaseSize;
+ modules.push_back( e );
+ keepGoing = !!pM32N( hSnap, &me );
+ }
+
+ CloseHandle(hSnap);
+ FreeLibrary(hToolhelp);
+
+ return modules.size() != 0;
+} // GetModuleListTH32
+
+
+// **************************************** PSAPI ************************
+typedef struct _MODULEINFO {
+ LPVOID lpBaseOfDll;
+ DWORD SizeOfImage;
+ LPVOID EntryPoint;
+} MODULEINFO, *LPMODULEINFO;
+
+static bool GetModuleListPSAPI(ModuleList &modules, DWORD pid, HANDLE hProcess, FILE *fLogFile)
+{
+ // EnumProcessModules()
+ typedef BOOL (__stdcall *tEPM)(HANDLE hProcess, HMODULE *lphModule, DWORD cb, LPDWORD lpcbNeeded );
+ // GetModuleFileNameEx()
+ typedef DWORD (__stdcall *tGMFNE)(HANDLE hProcess, HMODULE hModule, LPSTR lpFilename, DWORD nSize );
+ // GetModuleBaseName()
+ typedef DWORD (__stdcall *tGMBN)(HANDLE hProcess, HMODULE hModule, LPSTR lpFilename, DWORD nSize );
+ // GetModuleInformation()
+ typedef BOOL (__stdcall *tGMI)(HANDLE hProcess, HMODULE hModule, LPMODULEINFO pmi, DWORD nSize );
+
+ HINSTANCE hPsapi;
+ tEPM pEPM;
+ tGMFNE pGMFNE;
+ tGMBN pGMBN;
+ tGMI pGMI;
+
+ DWORD i;
+ ModuleEntry e;
+ DWORD cbNeeded;
+ MODULEINFO mi;
+ HMODULE *hMods = 0;
+ char *tt = 0;
+
+ hPsapi = LoadLibrary( _T("psapi.dll") );
+ if ( hPsapi == 0 )
+ return false;
+
+ modules.clear();
+
+ pEPM = (tEPM) GetProcAddress( hPsapi, "EnumProcessModules" );
+ pGMFNE = (tGMFNE) GetProcAddress( hPsapi, "GetModuleFileNameExA" );
+ pGMBN = (tGMFNE) GetProcAddress( hPsapi, "GetModuleBaseNameA" );
+ pGMI = (tGMI) GetProcAddress( hPsapi, "GetModuleInformation" );
+ if ( pEPM == 0 || pGMFNE == 0 || pGMBN == 0 || pGMI == 0 )
+ {
+ // we couldn´t find all functions
+ FreeLibrary( hPsapi );
+ return false;
+ }
+
+ hMods = (HMODULE*) malloc(sizeof(HMODULE) * (TTBUFLEN / sizeof HMODULE));
+ tt = (char*) malloc(sizeof(char) * TTBUFLEN);
+
+ if ( ! pEPM( hProcess, hMods, TTBUFLEN, &cbNeeded ) )
+ {
+ _ftprintf(fLogFile, _T("%lu: EPM failed, GetLastError = %lu\n"), g_dwShowCount, gle );
+ goto cleanup;
+ }
+
+ if ( cbNeeded > TTBUFLEN )
+ {
+ _ftprintf(fLogFile, _T("%lu: More than %lu module handles. Huh?\n"), g_dwShowCount, lenof( hMods ) );
+ goto cleanup;
+ }
+
+ for ( i = 0; i < cbNeeded / sizeof hMods[0]; i++ )
+ {
+ // base address, size
+ pGMI(hProcess, hMods[i], &mi, sizeof mi );
+ e.baseAddress = (DWORD) mi.lpBaseOfDll;
+ e.size = mi.SizeOfImage;
+ // image file name
+ tt[0] = 0;
+ pGMFNE(hProcess, hMods[i], tt, TTBUFLEN );
+ e.imageName = tt;
+ // module name
+ tt[0] = 0;
+ pGMBN(hProcess, hMods[i], tt, TTBUFLEN );
+ e.moduleName = tt;
+
+ modules.push_back(e);
+ }
+
+cleanup:
+ if (hPsapi)
+ FreeLibrary(hPsapi);
+ free(tt);
+ free(hMods);
+
+ return modules.size() != 0;
+} // GetModuleListPSAPI
+
+
+static bool GetModuleList(ModuleList& modules, DWORD pid, HANDLE hProcess, FILE *fLogFile)
+{
+ // first try toolhelp32
+ if (GetModuleListTH32(modules, pid, fLogFile) )
+ return true;
+ // then try psapi
+ return GetModuleListPSAPI(modules, pid, hProcess, fLogFile);
+} // GetModuleList
+
+
+static void EnumAndLoadModuleSymbols( HANDLE hProcess, DWORD pid, FILE *fLogFile )
+{
+ static ModuleList modules;
+ static ModuleListIter it;
+ char *img, *mod;
+
+ // fill in module list
+ GetModuleList(modules, pid, hProcess, fLogFile);
+
+ for ( it = modules.begin(); it != modules.end(); ++ it )
+ {
+ // SymLoadModule() wants writeable strings
+ img = strdup(it->imageName.c_str());
+ mod = strdup(it->moduleName.c_str());
+
+ pSLM( hProcess, 0, img, mod, it->baseAddress, it->size );
+
+ free(img);
+ free(mod);
+ std::string s;
+ }
+} // EnumAndLoadModuleSymbols
+
+static int InitStackWalk(void)
+{
+ if (g_bInitialized != FALSE)
+ return 0; // already initialized
+
+ // 02-12-19: Now we only support dbghelp.dll!
+ // To use it on NT you have to install the redistrubutable for DBGHELP.DLL
+ g_hImagehlpDll = LoadLibrary( _T("dbghelp.dll") );
+ if ( g_hImagehlpDll == NULL )
+ {
+ printf( "LoadLibrary( \"dbghelp.dll\" ): GetLastError = %lu\n", gle );
+ g_bInitialized = FALSE;
+ return 1;
+ }
+
+ // now we only support the newer dbghlp.dll with the "64"-functions (StackWalk64, a.s.o.)
+ // If your dbghlp.dll does not support this, please download the redistributable from MS
+ // Normally from: http://www.microsoft.com/downloads/details.aspx?displaylang=en&FamilyID=CD1FC4B2-0885-47F4-AF45-7FD5E14DB6C0
+
+ pSC = (tSC) GetProcAddress( g_hImagehlpDll, "SymCleanup" );
+ pSFTA = (tSFTA) GetProcAddress( g_hImagehlpDll, "SymFunctionTableAccess64" );
+ pSGLFA = (tSGLFA) GetProcAddress( g_hImagehlpDll, "SymGetLineFromAddr64" );
+ pSGMB = (tSGMB) GetProcAddress( g_hImagehlpDll, "SymGetModuleBase64" );
+ pSGMI = (tSGMI) GetProcAddress( g_hImagehlpDll, "SymGetModuleInfo64" );
+ pSGO = (tSGO) GetProcAddress( g_hImagehlpDll, "SymGetOptions" );
+ pSGSFA = (tSGSFA) GetProcAddress( g_hImagehlpDll, "SymGetSymFromAddr64" );
+ pSI = (tSI) GetProcAddress( g_hImagehlpDll, "SymInitialize" );
+ pSSO = (tSSO) GetProcAddress( g_hImagehlpDll, "SymSetOptions" );
+ pSW = (tSW) GetProcAddress( g_hImagehlpDll, "StackWalk64" );
+ pUDSN = (tUDSN) GetProcAddress( g_hImagehlpDll, "UnDecorateSymbolName" );
+ pSLM = (tSLM) GetProcAddress( g_hImagehlpDll, "SymLoadModule64" );
+
+ if ( pSC == NULL || pSFTA == NULL || pSGMB == NULL || pSGMI == NULL ||
+ pSGO == NULL || pSGSFA == NULL || pSI == NULL || pSSO == NULL ||
+ pSW == NULL || pUDSN == NULL || pSLM == NULL )
+ {
+ printf( "GetProcAddress(): some required function not found.\n" );
+ FreeLibrary( g_hImagehlpDll );
+ g_bInitialized = FALSE;
+ return 1;
+ }
+
+ g_bInitialized = TRUE;
+ InitializeCriticalSection(&g_csFileOpenClose);
+ return 0;
+}
+
+// This function if NOT multi-threading capable
+// It should only be called from the main-Function!
+int InitAllocCheckWN(eAllocCheckOutput eOutput, LPCTSTR pszFileName, ULONG ulShowStackAtAlloc) {
+ if (g_bInitialized) {
+ return 2; // already initialized!
+ }
+ if (ulShowStackAtAlloc <= 3)
+ g_ulShowStackAtAlloc = ulShowStackAtAlloc;
+ else
+ g_ulShowStackAtAlloc = 0;
+
+ if (pszFileName != NULL)
+ g_pszAllocLogName = _tcsdup(pszFileName);
+ else
+ g_pszAllocLogName = NULL;
+
+ g_CallstackOutputType = eOutput;
+
+#ifdef _DEBUG
+ AllocHashInit();
+
+#ifdef WITH_IMALLOC_SPY
+ HRESULT hr;
+ // erzeuge mein malloc-Spy object
+ LPMALLOCSPY pMallocSpy = new CMallocSpy(); // wird später durch Release freigegeben
+ if (pMallocSpy != NULL)
+ {
+ // CoInitilize(); // ??? Ist dies notwendig ?
+ hr = CoRegisterMallocSpy(pMallocSpy);
+ if FAILED(hr)
+ {
+ _tprintf(_T("\nCoRegisterMallocSpay failed with %.8x"), hr);
+ }
+ }
+#endif
+
+ // save the previous alloc hook
+ pfnOldCrtAllocHook = _CrtSetAllocHook(MyAllocHook);
+#endif
+
+ return InitStackWalk();
+} // InitAllocCheckWN
+
+static TCHAR s_szExceptionLogFileName[_MAX_PATH] = _T("\\exceptions.log"); // default
+static BOOL s_bUnhandledExeptionFilterSet = FALSE;
+static LONG __stdcall CrashHandlerExceptionFilter(EXCEPTION_POINTERS* pExPtrs)
+{
+ if (pExPtrs->ExceptionRecord->ExceptionCode == EXCEPTION_STACK_OVERFLOW)
+ {
+ static char MyStack[1024*128]; // be sure that we have enought space...
+ // it assumes that DS and SS are the same!!! (this is the case for Win32)
+ // change the stack only if the selectors are the same (this is the case for Win32)
+ //__asm push offset MyStack[1024*128];
+ //__asm pop esp;
+ __asm mov eax,offset MyStack[1024*128];
+ __asm mov esp,eax;
+ }
+
+ LONG lRet;
+ lRet = StackwalkFilter(pExPtrs, /*EXCEPTION_CONTINUE_SEARCH*/EXCEPTION_EXECUTE_HANDLER, s_szExceptionLogFileName);
+ TCHAR lString[500];
+ _stprintf(lString,
+ _T("*** Unhandled Exception!\n")
+ _T(" ExpCode: 0x%8.8X\n")
+ _T(" ExpFlags: %d\n")
+ _T(" ExpAddress: 0x%8.8X\n")
+ _T(" Please report!"),
+ pExPtrs->ExceptionRecord->ExceptionCode,
+ pExPtrs->ExceptionRecord->ExceptionFlags,
+ pExPtrs->ExceptionRecord->ExceptionAddress);
+ FatalAppExit(-1,lString);
+ return lRet;
+}
+
+int InitAllocCheck(eAllocCheckOutput eOutput, BOOL bSetUnhandledExeptionFilter, ULONG ulShowStackAtAlloc) // will create the filename by itself
+{
+ TCHAR szModName[_MAX_PATH];
+ if (GetModuleFileName(NULL, szModName, sizeof(szModName)/sizeof(TCHAR)) != 0)
+ {
+ _tcscpy(s_szExceptionLogFileName, szModName);
+ if (eOutput == ACOutput_XML)
+ _tcscat(s_szExceptionLogFileName, _T(".exp.xml"));
+ else
+ _tcscat(s_szExceptionLogFileName, _T(".exp.log"));
+
+ if (eOutput == ACOutput_XML)
+ _tcscat(szModName, _T(".mem.xml-leaks"));
+ else
+ _tcscat(szModName, _T(".mem.log"));
+ }
+ else
+ {
+ if (eOutput == ACOutput_XML)
+ _tcscpy(szModName, _T("\\mem-leaks.xml-leaks")); // default
+ else
+ _tcscpy(szModName, _T("\\mem-leaks.log")); // default
+ }
+
+ if ((bSetUnhandledExeptionFilter != FALSE) && (s_bUnhandledExeptionFilterSet == FALSE) )
+ {
+ // set global exception handler (for handling all unhandled exceptions)
+ SetUnhandledExceptionFilter(CrashHandlerExceptionFilter);
+ s_bUnhandledExeptionFilterSet = TRUE;
+ }
+
+ return InitAllocCheckWN(eOutput, szModName, ulShowStackAtAlloc);
+}
+
+
+// This function if NOT multi-threading capable
+// It should only be called from the main-Function!
+// Returns the number of bytes that are not freed (leaks)
+ULONG DeInitAllocCheck(void) {
+ ULONG ulRet = 0;
+ if (g_bInitialized) {
+
+#ifdef _DEBUG
+ InterlockedIncrement(&g_lMallocCalled); // No deactivate MyAllocHook, because StackWalker will allocate some memory)
+ ulRet = AllocHashDeinit(); // output the not freed memory
+ // remove the hook and set the old one
+ _CrtSetAllocHook(pfnOldCrtAllocHook);
+
+#ifdef WITH_IMALLOC_SPY
+ CoRevokeMallocSpy();
+#endif
+
+#endif
+
+ EnterCriticalSection(&g_csFileOpenClose); // wait until a running stack dump was created
+ g_bInitialized = FALSE;
+
+ // de-init symbol handler etc. (SymCleanup())
+ if (pSC != NULL)
+ pSC( GetCurrentProcess() );
+ FreeLibrary( g_hImagehlpDll );
+
+ LeaveCriticalSection(&g_csFileOpenClose);
+ if (g_pszAllocLogName != NULL) {
+ free(g_pszAllocLogName);
+ g_pszAllocLogName = NULL;
+ }
+ if (g_fFile != NULL) {
+ fclose(g_fFile);
+ g_fFile = NULL;
+ }
+
+ DeleteCriticalSection(&g_csFileOpenClose);
+ InterlockedDecrement(&g_lMallocCalled);
+ }
+
+ if (s_bUnhandledExeptionFilterSet != TRUE)
+ {
+ SetUnhandledExceptionFilter(NULL);
+ s_bUnhandledExeptionFilterSet = FALSE;
+ }
+ return ulRet;
+} // DeInitAllocCheck
+
+
+
+void OnlyInstallUnhandeldExceptionFilter(eAllocCheckOutput eOutput)
+{
+ if (s_bUnhandledExeptionFilterSet == FALSE)
+ {
+ TCHAR szModName[_MAX_PATH];
+ if (GetModuleFileName(NULL, szModName, sizeof(szModName)/sizeof(TCHAR)) != 0)
+ {
+ _tcscpy(s_szExceptionLogFileName, szModName);
+ if (eOutput == ACOutput_XML)
+ _tcscat(s_szExceptionLogFileName, _T(".exp.xml"));
+ else
+ _tcscat(s_szExceptionLogFileName, _T(".exp.log"));
+
+ if (eOutput == ACOutput_XML)
+ _tcscat(szModName, _T(".mem.xml-leaks"));
+ else
+ _tcscat(szModName, _T(".mem.log"));
+ }
+ else
+ {
+ if (eOutput == ACOutput_XML)
+ _tcscpy(szModName, _T("\\mem-leaks.xml-leaks")); // default
+ else
+ _tcscpy(szModName, _T("\\mem-leaks.log")); // default
+ }
+ // set it again; WARNING: this will override the setting for a possible AllocCheck-Setting
+ g_CallstackOutputType = eOutput;
+
+ // set global exception handler (for handling all unhandled exceptions)
+ SetUnhandledExceptionFilter(CrashHandlerExceptionFilter);
+ s_bUnhandledExeptionFilterSet = TRUE;
+ }
+}
+
+
+
+static TCHAR *GetExpectionCodeText(DWORD dwExceptionCode) {
+ switch(dwExceptionCode) {
+ case EXCEPTION_ACCESS_VIOLATION: return _T("ACCESS VIOLATION");
+ case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: return _T("ARRAY BOUNDS EXCEEDED");
+ case EXCEPTION_BREAKPOINT: return _T("BREAKPOINT");
+ case EXCEPTION_DATATYPE_MISALIGNMENT: return _T("DATATYPE MISALIGNMENT");
+ case EXCEPTION_FLT_DENORMAL_OPERAND: return _T("FLT DENORMAL OPERAND");
+ case EXCEPTION_FLT_DIVIDE_BY_ZERO: return _T("FLT DIVIDE BY ZERO");
+ case EXCEPTION_FLT_INEXACT_RESULT: return _T("FLT INEXACT RESULT");
+ case EXCEPTION_FLT_INVALID_OPERATION: return _T("FLT INVALID OPERATION");
+ case EXCEPTION_FLT_OVERFLOW: return _T("FLT OVERFLOW");
+ case EXCEPTION_FLT_STACK_CHECK: return _T("FLT STACK CHECK");
+ case EXCEPTION_FLT_UNDERFLOW: return _T("FLT UNDERFLOW");
+ case EXCEPTION_ILLEGAL_INSTRUCTION: return _T("ILLEGAL INSTRUCTION");
+ case EXCEPTION_IN_PAGE_ERROR: return _T("IN PAGE ERROR");
+ case EXCEPTION_INT_DIVIDE_BY_ZERO: return _T("INT DIVIDE BY ZERO");
+ case EXCEPTION_INT_OVERFLOW: return _T("INT OVERFLOW");
+ case EXCEPTION_INVALID_DISPOSITION: return _T("INVALID DISPOSITION");
+ case EXCEPTION_NONCONTINUABLE_EXCEPTION: return _T("NONCONTINUABLE EXCEPTION");
+ case EXCEPTION_PRIV_INSTRUCTION: return _T("PRIV INSTRUCTION");
+ case EXCEPTION_SINGLE_STEP: return _T("SINGLE STEP");
+ case EXCEPTION_STACK_OVERFLOW: return _T("STACK OVERFLOW");
+ case DBG_CONTROL_C : return _T("DBG CONTROL C ");
+ default:
+ return _T("<unkown exception>");
+ }
+} // GetExpectionCodeText
+
+// Function is not multi-threading safe, because of static char!
+static TCHAR *GetAdditionalExpectionCodeText(PEXCEPTION_RECORD pExceptionRecord) {
+ static TCHAR szTemp[100];
+
+ switch(pExceptionRecord->ExceptionCode) {
+ case EXCEPTION_ACCESS_VIOLATION:
+ if (pExceptionRecord->NumberParameters == 2) {
+ switch(pExceptionRecord->ExceptionInformation[0]) {
+ case 0: // read attempt
+ _stprintf(szTemp, _T(" read attempt to address 0x%8.8X "), pExceptionRecord->ExceptionInformation[1]);
+ return szTemp;
+ case 1: // write attempt
+ _stprintf(szTemp, _T(" write attempt to address 0x%8.8X "), pExceptionRecord->ExceptionInformation[1]);
+ return szTemp;
+ default:
+ return _T("");
+ }
+ } // if (pExceptionRecord->NumberParameters == 2)
+ return _T("");
+ default:
+ return _T("");
+ } // switch(pExceptionRecord->ExceptionCode)
+} // GetAdditionalExpectionCodeText
+
+std::string SimpleXMLEncode(PCSTR szText)
+{
+ std::string szRet;
+
+ for (size_t i=0; i<strlen(szText); i++)
+ {
+ switch(szText[i])
+ {
+ case '&':
+ szRet.append("&");
+ break;
+ case '<':
+ szRet.append("<");
+ break;
+ case '>':
+ szRet.append(">");
+ break;
+ case '"':
+ szRet.append(""");
+ break;
+ case '\'':
+ szRet.append("'");
+ break;
+ default:
+ szRet += szText[i];
+ }
+ }
+ return szRet;
+}
+
+
+// #################################################################################
+// #################################################################################
+// Here the Stackwalk-Part begins.
+// Some of the code is from an example from a book
+// But I couldn´t find the reference anymore... sorry...
+// If someone knowns, please let me know...
+// #################################################################################
+// #################################################################################
+
+
+// if you use C++ exception handling: install a translator function
+// with set_se_translator(). In the context of that function (but *not*
+// afterwards), you can either do your stack dump, or save the CONTEXT
+// record as a local copy. Note that you must do the stack sump at the
+// earliest opportunity, to avoid the interesting stackframes being gone
+// by the time you do the dump.
+
+// status:
+// - EXCEPTION_CONTINUE_SEARCH: exception wird weitergereicht
+// - EXCEPTION_CONTINUE_EXECUTION:
+// - EXCEPTION_EXECUTE_HANDLER:
+DWORD StackwalkFilter( EXCEPTION_POINTERS *ep, DWORD status, LPCTSTR pszLogFile)
+{
+ HANDLE hThread;
+ FILE *fFile = stdout; // default to stdout
+
+ if (pszLogFile != NULL) { // a filename is provided
+ // Open the logfile
+ fFile = _tfopen(pszLogFile, _T("a"));
+ if (fFile != NULL) { // Is the file too big?
+ long size;
+ fseek(fFile, 0, SEEK_END);
+ size = ftell(fFile); // Get the size of the file
+ if (size >= LOG_FILE_MAX_SIZE) {
+ TCHAR *pszTemp = (TCHAR*) malloc(MAX_PATH);
+ // It is too big...
+ fclose(fFile);
+ _tcscpy(pszTemp, pszLogFile);
+ _tcscat(pszTemp, _T(".old"));
+ _tremove(pszTemp); // Remove an old file, if exists
+ _trename(pszLogFile, pszTemp); // rename the actual file
+ fFile = _tfopen(pszLogFile, _T("w")); // create a new file
+ free(pszTemp);
+ }
+ }
+ } // if (pszLogFile != NULL)
+ if (fFile == NULL) {
+ fFile = stdout;
+ }
+
+ // Write infos about the exception
+ if (g_CallstackOutputType == ACOutput_XML)
+ {
+ _ftprintf(fFile, _T("<EXCEPTION code=\"%8.8X\" addr=\"%8.8X\" "),
+ ep->ExceptionRecord->ExceptionCode,
+ ep->ExceptionRecord->ExceptionAddress);
+ WriteDateTime(fFile, TRUE);
+ _ftprintf(fFile, _T("code_desc=\"%s\" more_desc=\"%s\">\n"), GetExpectionCodeText(ep->ExceptionRecord->ExceptionCode),
+ GetAdditionalExpectionCodeText(ep->ExceptionRecord));
+ }
+ else
+ {
+ _ftprintf(fFile, _T("######## EXCEPTION: 0x%8.8X at address: 0x%8.8X"),
+ ep->ExceptionRecord->ExceptionCode,
+ ep->ExceptionRecord->ExceptionAddress);
+ _ftprintf(fFile, _T(": %s %s\n"), GetExpectionCodeText(ep->ExceptionRecord->ExceptionCode),
+ GetAdditionalExpectionCodeText(ep->ExceptionRecord));
+ }
+
+ DuplicateHandle( GetCurrentProcess(), GetCurrentThread(),
+ GetCurrentProcess(), &hThread, 0, false, DUPLICATE_SAME_ACCESS );
+ ShowStack( hThread, *(ep->ContextRecord), fFile);
+ CloseHandle( hThread );
+
+ if (g_CallstackOutputType == ACOutput_XML)
+ _ftprintf(fFile, _T("</EXCEPTION>\n"));
+
+ fclose(fFile);
+
+ return status;
+} // StackwalkFilter
+
+void ShowStack( HANDLE hThread, CONTEXT& c, LPCTSTR pszLogFile)
+{
+ FILE *fFile = stdout; // default to stdout
+
+ if (pszLogFile != NULL) { // a filename is available
+ // Open the logfile
+ fFile = _tfopen(pszLogFile, _T("a"));
+ if (fFile != NULL) { // Is the file too big?
+ long size;
+ fseek(fFile, 0, SEEK_END);
+ size = ftell(fFile); // Get the size of the file
+ if (size >= LOG_FILE_MAX_SIZE) {
+ TCHAR *pszTemp = (TCHAR*) malloc(MAX_PATH);
+ // It is too big...
+ fclose(fFile);
+ _tcscpy(pszTemp, pszLogFile);
+ _tcscat(pszTemp, _T(".old"));
+ _tremove(pszTemp); // Remove an old file, if exists
+ _trename(pszLogFile, pszTemp); // rename the actual file
+ fFile = _tfopen(pszLogFile, _T("w")); // open new file
+ free(pszTemp);
+ }
+ }
+ } // if (pszLogFile != NULL)
+ if (fFile == NULL) {
+ fFile = stdout;
+ }
+
+ ShowStack( hThread, c, fFile);
+
+ fclose(fFile);
+}
+
+
+static void ShowStack( HANDLE hThread, CONTEXT& c, FILE *fLogFile) {
+ ShowStackRM(hThread, c, fLogFile, NULL, GetCurrentProcess());
+}
+
+static void ShowStackRM( HANDLE hThread, CONTEXT& c, FILE *fLogFile, PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryFunction, HANDLE hSWProcess) {
+ // normally, call ImageNtHeader() and use machine info from PE header
+ // but we assume that it is an I386 image...
+ DWORD imageType = IMAGE_FILE_MACHINE_I386;
+ HANDLE hProcess = GetCurrentProcess(); // hProcess normally comes from outside but we only do the stackdump in our own process
+ int frameNum; // counts walked frames
+ DWORD64 offsetFromSymbol; // tells us how far from the symbol we were
+ DWORD offsetFromLine; // tells us how far from the line we were
+ DWORD symOptions; // symbol handler settings
+
+ static IMAGEHLP_SYMBOL64 *pSym = NULL;
+ char undName[MAXNAMELEN]; // undecorated name
+ char undFullName[MAXNAMELEN]; // undecorated name with all shenanigans
+ IMAGEHLP_MODULE64 Module;
+ IMAGEHLP_LINE64 Line;
+ BOOL bXMLTagWrote;
+
+ std::string symSearchPath;
+
+ static bool bFirstTime = TRUE;
+
+ // If no logfile is present, outpur to "stdout"
+ if (fLogFile == NULL) {
+ fLogFile = stdout;
+ }
+
+ STACKFRAME64 s; // in/out stackframe
+ memset( &s, '\0', sizeof s );
+
+ if ( (g_bInitialized == FALSE) && (bFirstTime == TRUE) ) {
+ InitStackWalk();
+ }
+
+ if (g_bInitialized == FALSE)
+ {
+ // Could not init!!!!
+ bFirstTime = FALSE;
+ _ftprintf(fLogFile, _T("%lu: Stackwalker not initialized (or was not able to initialize)!\n"), g_dwShowCount);
+ return;
+ }
+
+// Critical section begin...
+ EnterCriticalSection(&g_csFileOpenClose);
+
+ InterlockedIncrement((long*) &g_dwShowCount); // erhöhe counter
+
+
+ // NOTE: normally, the exe directory and the current directory should be taken
+ // from the target process. The current dir would be gotten through injection
+ // of a remote thread; the exe fir through either ToolHelp32 or PSAPI.
+
+ if (pSym == NULL) {
+ pSym = (IMAGEHLP_SYMBOL64 *) malloc( IMGSYMLEN + MAXNAMELEN );
+ if (!pSym) goto cleanup; // not enough memory...
+ }
+
+ if (g_CallstackOutputType != ACOutput_XML)
+ {
+ _ftprintf(fLogFile, _T("%lu: "), g_dwShowCount);
+ WriteDateTime(fLogFile);
+ _ftprintf(fLogFile, _T("\n"));
+ }
+
+
+ if (bFirstTime) {
+
+ CHAR *tt, *p;
+
+ tt = (CHAR*) malloc(sizeof(CHAR) * TTBUFLEN); // Get the temporary buffer
+ if (!tt) goto cleanup; // not enough memory...
+
+ // build symbol search path from:
+ symSearchPath = "";
+ // current directory
+ if ( GetCurrentDirectoryA( TTBUFLEN, tt ) )
+ symSearchPath += tt + std::string( ";" );
+ // dir with executable
+ if ( GetModuleFileNameA( 0, tt, TTBUFLEN ) )
+ {
+ for ( p = tt + strlen( tt ) - 1; p >= tt; -- p )
+ {
+ // locate the rightmost path separator
+ if ( *p == '\\' || *p == '/' || *p == ':' )
+ break;
+ }
+ // if we found one, p is pointing at it; if not, tt only contains
+ // an exe name (no path), and p points before its first byte
+ if ( p != tt ) // path sep found?
+ {
+ if ( *p == ':' ) // we leave colons in place
+ ++ p;
+ *p = '\0'; // eliminate the exe name and last path sep
+ symSearchPath += tt + std::string( ";" );
+ }
+ }
+ // environment variable _NT_SYMBOL_PATH
+ if ( GetEnvironmentVariableA( "_NT_SYMBOL_PATH", tt, TTBUFLEN ) )
+ symSearchPath += tt + std::string( ";" );
+ // environment variable _NT_ALTERNATE_SYMBOL_PATH
+ if ( GetEnvironmentVariableA( "_NT_ALTERNATE_SYMBOL_PATH", tt, TTBUFLEN ) )
+ symSearchPath += tt + std::string( ";" );
+ // environment variable SYSTEMROOT
+ if ( GetEnvironmentVariableA( "SYSTEMROOT", tt, TTBUFLEN ) )
+ symSearchPath += tt + std::string( ";" );
+
+
+
+ if ( symSearchPath.size() > 0 ) // if we added anything, we have a trailing semicolon
+ symSearchPath = symSearchPath.substr( 0, symSearchPath.size() - 1 );
+
+ // why oh why does SymInitialize() want a writeable string?
+ strncpy( tt, symSearchPath.c_str(), TTBUFLEN );
+ tt[TTBUFLEN - 1] = '\0'; // if strncpy() overruns, it doesn't add the null terminator
+
+ // init symbol handler stuff (SymInitialize())
+ if ( ! pSI( hProcess, tt, false ) )
+ {
+ if (g_CallstackOutputType != ACOutput_XML)
+ _ftprintf(fLogFile, _T("%lu: SymInitialize(): GetLastError = %lu\n"), g_dwShowCount, gle );
+ if (tt) free( tt );
+ goto cleanup;
+ }
+
+ // SymGetOptions()
+ symOptions = pSGO();
+ symOptions |= SYMOPT_LOAD_LINES;
+ symOptions &= ~SYMOPT_UNDNAME;
+ symOptions &= ~SYMOPT_DEFERRED_LOADS;
+ pSSO( symOptions ); // SymSetOptions()
+
+ // Enumerate modules and tell dbghlp.dll about them.
+ // On NT, this is not necessary, but it won't hurt.
+ EnumAndLoadModuleSymbols( hProcess, GetCurrentProcessId(), fLogFile );
+
+ if (tt)
+ free( tt );
+ } // bFirstTime = TRUE
+ bFirstTime = FALSE;
+
+ // init STACKFRAME for first call
+ // Notes: AddrModeFlat is just an assumption. I hate VDM debugging.
+ // Notes: will have to be #ifdef-ed for Alphas; MIPSes are dead anyway,
+ // and good riddance.
+ s.AddrPC.Offset = c.Eip;
+ s.AddrPC.Mode = AddrModeFlat;
+ s.AddrFrame.Offset = c.Ebp;
+ s.AddrFrame.Mode = AddrModeFlat;
+ s.AddrStack.Offset = c.Ebp;
+ s.AddrStack.Mode = AddrModeFlat;
+
+ memset( pSym, '\0', IMGSYMLEN + MAXNAMELEN );
+ pSym->SizeOfStruct = IMGSYMLEN;
+ pSym->MaxNameLength = MAXNAMELEN;
+
+ memset( &Line, '\0', sizeof Line );
+ Line.SizeOfStruct = sizeof Line;
+
+ memset( &Module, '\0', sizeof Module );
+ Module.SizeOfStruct = sizeof Module;
+
+ for ( frameNum = 0; ; ++ frameNum )
+ {
+ // get next stack frame (StackWalk64(), SymFunctionTableAccess64(), SymGetModuleBase64())
+ // if this returns ERROR_INVALID_ADDRESS (487) or ERROR_NOACCESS (998), you can
+ // assume that either you are done, or that the stack is so hosed that the next
+ // deeper frame could not be found.
+ // CONTEXT need not to be suplied if imageTyp is IMAGE_FILE_MACHINE_I386!
+ if ( ! pSW( imageType, hSWProcess, hThread, &s, NULL, ReadMemoryFunction, pSFTA, pSGMB, NULL ) )
+ break;
+
+ bXMLTagWrote = FALSE;
+
+ if (g_CallstackOutputType == ACOutput_Advanced)
+ _ftprintf(fLogFile, _T("\n%lu: %3d"), g_dwShowCount, frameNum);
+ if ( s.AddrPC.Offset == 0 )
+ {
+ // Special case: If we are here, we have no valid callstack entry!
+ switch(g_CallstackOutputType)
+ {
+ case ACOutput_Simple:
+ _ftprintf(fLogFile, _T("%lu: (-nosymbols- PC == 0)\n"), g_dwShowCount);
+ break;
+ case ACOutput_Advanced:
+ _ftprintf(fLogFile, _T(" (-nosymbols- PC == 0)\n"));
+ break;
+ case ACOutput_XML:
+ // TODO: ....
+ _ftprintf(fLogFile, _T("<STACKENTRY decl=\"(-nosymbols- PC == 0)\"/>\n"));
+ break;
+ }
+ }
+ else
+ {
+ // we seem to have a valid PC
+ undName[0] = 0;
+ undFullName[0] = 0;
+ offsetFromSymbol = 0;
+ // show procedure info (SymGetSymFromAddr())
+ if ( ! pSGSFA( hProcess, s.AddrPC.Offset, &offsetFromSymbol, pSym ) )
+ {
+ if (g_CallstackOutputType == ACOutput_Advanced)
+ {
+ if ( gle != 487 )
+ _ftprintf(fLogFile, _T(" SymGetSymFromAddr(): GetLastError = %lu\n"), gle );
+ else
+ _ftprintf(fLogFile, _T("\n"));
+ }
+ }
+ else
+ {
+ // UnDecorateSymbolName()
+ pUDSN( pSym->Name, undName, MAXNAMELEN, UNDNAME_NAME_ONLY );
+ pUDSN( pSym->Name, undFullName, MAXNAMELEN, UNDNAME_COMPLETE );
+ if (g_CallstackOutputType == ACOutput_Advanced)
+ {
+ if (strlen(undName) > 0)
+ fprintf(fLogFile, " %s %+ld bytes\n", undName, (long) offsetFromSymbol );
+ else
+ {
+ fprintf(fLogFile, " Sig: %s %+ld bytes\n", pSym->Name, (long) offsetFromSymbol );
+ strcpy(undName, pSym->Name);
+ }
+ fprintf(fLogFile, "%lu: Decl: %s\n", g_dwShowCount, undFullName );
+ }
+ }
+ //if (g_CallstackOutputType == ACOutput_XML)
+ // fprintf(fLogFile, "decl=\"%s\" decl_offset=\"%+ld\" ", SimpleXMLEncode(undName).c_str(), (long) offsetFromSymbol);
+
+ // show line number info, NT5.0-method (SymGetLineFromAddr())
+ offsetFromLine = 0;
+ if ( pSGLFA != NULL )
+ { // yes, we have SymGetLineFromAddr()
+ if ( ! pSGLFA( hProcess, s.AddrPC.Offset, &offsetFromLine, &Line ) )
+ {
+ if ( (gle != 487) && (frameNum > 0) ) // ignore error for first frame
+ {
+ if (g_CallstackOutputType == ACOutput_XML)
+ {
+ _ftprintf(fLogFile, _T("<STACKENTRY "));
+ bXMLTagWrote = TRUE;
+ fprintf(fLogFile, "decl=\"%s\" decl_offset=\"%+ld\" ", SimpleXMLEncode(undName).c_str(), (long) offsetFromSymbol);
+ _ftprintf(fLogFile, _T("srcfile=\"SymGetLineFromAddr(): GetLastError = %lu\" "), gle);
+ }
+ else
+ _ftprintf(fLogFile, _T("%lu: SymGetLineFromAddr(): GetLastError = %lu\n"), g_dwShowCount, gle );
+ }
+ }
+ else
+ {
+ switch(g_CallstackOutputType)
+ {
+ case ACOutput_Advanced:
+ fprintf(fLogFile, "%lu: Line: %s(%lu) %+ld bytes\n", g_dwShowCount,
+ Line.FileName, Line.LineNumber, offsetFromLine );
+ break;
+ case ACOutput_Simple:
+ fprintf(fLogFile, "%lu: %s(%lu) %+ld bytes (%s)\n", g_dwShowCount,
+ Line.FileName, Line.LineNumber, offsetFromLine, undName);
+ break;
+ case ACOutput_XML:
+ _ftprintf(fLogFile, _T("<STACKENTRY "));
+ bXMLTagWrote = TRUE;
+ fprintf(fLogFile, "decl=\"%s\" decl_offset=\"%+ld\" ", SimpleXMLEncode(undName).c_str(), (long) offsetFromSymbol);
+ fprintf(fLogFile, "srcfile=\"%s\" line=\"%lu\" line_offset=\"%+ld\" ",
+ SimpleXMLEncode(Line.FileName).c_str(), Line.LineNumber, offsetFromLine, undName);
+ break;
+ }
+ }
+ } // yes, we have SymGetLineFromAddr()
+
+ // show module info (SymGetModuleInfo())
+ if ( (g_CallstackOutputType == ACOutput_Advanced) || (g_CallstackOutputType == ACOutput_XML) )
+ {
+ if ( ! pSGMI( hProcess, s.AddrPC.Offset, &Module ) )
+ {
+ if (g_CallstackOutputType == ACOutput_Advanced)
+ _ftprintf(fLogFile, _T("%lu: SymGetModuleInfo): GetLastError = %lu\n"), g_dwShowCount, gle );
+ }
+ else
+ { // got module info OK
+ char ty[80];
+ switch ( Module.SymType )
+ {
+ case SymNone:
+ strcpy( ty, "-nosymbols-" );
+ break;
+ case SymCoff:
+ strcpy( ty, "COFF" );
+ break;
+ case SymCv:
+ strcpy( ty, "CV" );
+ break;
+ case SymPdb:
+ strcpy( ty, "PDB" );
+ break;
+ case SymExport:
+ strcpy( ty, "-exported-" );
+ break;
+ case SymDeferred:
+ strcpy( ty, "-deferred-" );
+ break;
+ case SymSym:
+ strcpy( ty, "SYM" );
+ break;
+#if API_VERSION_NUMBER >= 9
+ case SymDia:
+ strcpy( ty, "DIA" );
+ break;
+#endif
+ default:
+ _snprintf( ty, sizeof ty, "symtype=%ld", (long) Module.SymType );
+ break;
+ }
+
+ if (g_CallstackOutputType == ACOutput_XML)
+ {
+ // now, check if the XML-Entry is written...
+ if (bXMLTagWrote == FALSE)
+ {
+ _ftprintf(fLogFile, _T("<STACKENTRY "));
+ bXMLTagWrote = TRUE;
+ fprintf(fLogFile, "decl=\"%s\" decl_offset=\"%+ld\" ", SimpleXMLEncode(undName).c_str(), (long) offsetFromSymbol);
+ _ftprintf(fLogFile, _T("srcfile=\"\" "));
+ bXMLTagWrote = TRUE;
+ }
+ }
+
+ if (g_CallstackOutputType == ACOutput_Advanced)
+ {
+ fprintf(fLogFile, "%lu: Mod: %s, base: %08lxh\n", g_dwShowCount,
+ Module.ModuleName, Module.BaseOfImage );
+ if (Module.SymType == SymNone) { // Gebe nur aus, wenn keine Symbole vorhanden sind!
+ _ftprintf(fLogFile, _T("%lu: Offset: 0x%8.8x\n"), g_dwShowCount, s.AddrPC.Offset);
+ fprintf(fLogFile, "%lu: Sym: type: %s, file: %s\n", g_dwShowCount,
+ ty, Module.LoadedImageName );
+ }
+ }
+ else
+ {
+ // XML:
+ if (bXMLTagWrote == TRUE)
+ fprintf(fLogFile, "module=\"%s\" base=\"%08lx\" ", Module.ModuleName, Module.BaseOfImage);
+ }
+ } // got module info OK
+ }
+ if ( (g_CallstackOutputType == ACOutput_XML) && (bXMLTagWrote == TRUE) )
+ _ftprintf(fLogFile, _T("/>\n")); // terminate the XML node
+
+ } // we seem to have a valid PC
+
+ // no return address means no deeper stackframe
+ if ( s.AddrReturn.Offset == 0 )
+ {
+ // avoid misunderstandings in the printf() following the loop
+ SetLastError( 0 );
+ break;
+ }
+
+ } // for ( frameNum )
+
+ if ( (g_CallstackOutputType != ACOutput_XML) && (gle != 0) )
+ _ftprintf(fLogFile, _T("\n%lu: StackWalk(): GetLastError = %lu\n"), g_dwShowCount, gle );
+
+cleanup:
+ //if (pSym) free( pSym );
+ if (fLogFile) {
+ _ftprintf(fLogFile, _T("\n\n"));
+ if (g_dwShowCount % 1000)
+ fflush(fLogFile);
+ }
+
+ LeaveCriticalSection(&g_csFileOpenClose);
+
+
+// Critical section end...
+} // ShowStackRM
+
+#pragma warning(pop)