X-Git-Url: https://gerrit.simantics.org/r/gitweb?a=blobdiff_plain;f=bundles%2Forg.simantics.databoard%2Fcpp%2FDataBoardTest%2FDataBoardTest%2FStackwalker.cpp;fp=bundles%2Forg.simantics.databoard%2Fcpp%2FDataBoardTest%2FDataBoardTest%2FStackwalker.cpp;h=254b1f0cfb18d33056bde19a9f3477102d2d9400;hb=0ae2b770234dfc3cbb18bd38f324125cf0faca07;hp=10afabe1e28049ba705c7fe2eeef08f1b3e59e09;hpb=24e2b34260f219f0d1644ca7a138894980e25b14;p=simantics%2Fplatform.git diff --git a/bundles/org.simantics.databoard/cpp/DataBoardTest/DataBoardTest/Stackwalker.cpp b/bundles/org.simantics.databoard/cpp/DataBoardTest/DataBoardTest/Stackwalker.cpp index 10afabe1e..254b1f0cf 100644 --- a/bundles/org.simantics.databoard/cpp/DataBoardTest/DataBoardTest/Stackwalker.cpp +++ b/bundles/org.simantics.databoard/cpp/DataBoardTest/DataBoardTest/Stackwalker.cpp @@ -1,2570 +1,2570 @@ -/*//////////////////////////////////////////////////////////////////////////// - * 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 -#include -#include -#include -#include -#include -#include -#include - -#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 -#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("\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("\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("\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("\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("\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("\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("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: -// -/* - * 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) - -// - - - - -// 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 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(""); - } -} // 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': - 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("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("\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("\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("= 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("\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) +/*//////////////////////////////////////////////////////////////////////////// + * 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 +#include +#include +#include +#include +#include +#include +#include + +#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 +#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("\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("\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("\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("\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("\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("\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("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: +// +/* + * 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) + +// + + + + +// 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 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(""); + } +} // 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': + 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("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("\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("\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("= 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("\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)