--- /dev/null
+/*////////////////////////////////////////////////////////////////////////////\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