Files
2022-10-26 12:25:11 +08:00

2543 lines
85 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*////////////////////////////////////////////////////////////////////////////
* 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-2003 (Freeware)
* http://www.codeproject.com/tools/leakfinder.asp
*
* License (The zlib/libpng License, http://www.opensource.org/licenses/zlib-license.php):
*
* Copyright (c) 2003 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 "UtilityCommon.h"
#if SUN_CODE_BACKUP
#include <windows.h>
#include <string>
#include <vector>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <crtdbg.h>
#include <tchar.h>
#if _MSC_VER <= 1310
#include "Stackwalker.h"
// If the following is defined, only the used memories are stored in the hash-table.
// If the memory is freed, it will be removed from the hash-table (to reduce memory)
// Consequences: At DeInitAllocHook, only Leaks will be reported
#define HASH_ENTRY_REMOVE_AT_FREE
// 0 = Do not write any output during runtime-alloc-call
// 1 = Write only the alloc action (malloc, realloc, free)
// 2 = Write alloc action and callstack only for malloc/realloc
// 3 = Write alloc action and callstack for all actions
static ULONG g_ulShowStackAtAlloc = 0;
// the form of the output file
static eAllocCheckOutput g_CallstackOutputType = ACOutput_Simple;
// Size of Hash-Table (this should be a prime number to avoid collisions)
#define ALLOC_HASH_ENTRIES 1023
// Size of Callstack-trace in bytes (0x500 => appr. 5-9 functions, depending on parameter count for each function)
#define MAX_ESP_LEN_BUF 0x500
// Normally we can ignore allocations from the Runtime-System
#define IGNORE_CRT_ALLOC
// MaxSize: 1 MByte (only for StackwalkFilter)
#define LOG_FILE_MAX_SIZE 1024*1024
// If the following is defined, then COM-Leaks will also be tracked
#define WITH_IMALLOC_SPY
// #############################################################################################
#ifdef WITH_IMALLOC_SPY
//forwards:
void IMallocHashInsert(void *pData, CONTEXT &Context, size_t nDataSize);
BOOL IMallocHashRemove(void *pData);
#pragma warning (push)
#pragma warning (disable: 4100)
// 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 ( GetThreadContext( hThread, &c ) != 0) {
// 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 ( GetThreadContext( hThread, &c ) != 0) {
// Ok
IMallocHashInsert(pActual, c, m_cbRequest);
}
CloseHandle(hThread);
}
return pActual;
}
STDMETHOD_(void *, PreGetSize) (void *pRequest, BOOL fSpyed) {
return pRequest;
}
STDMETHOD_(ULONG, PostGetSize) (ULONG cbActual, BOOL fSpyed) {
return cbActual;
}
STDMETHOD_(void *, PreDidAlloc) (void *pRequest, BOOL fSpyed) {
return pRequest;
}
STDMETHOD_(BOOL, PostDidAlloc) (void *pRequest, BOOL fSpyed, BOOL fActual) {
return fActual;
}
STDMETHOD_(void, PreHeapMinimize) (void) {
return;
}
STDMETHOD_(void, PostHeapMinimize) (void) {
return;
}
private:
LONG m_cRef;
ULONG m_cbRequest;
};
#endif
// #############################################################################################
// Here I have included the API-Version 9 declarations, so it will also compile on systems, where the new PSDK is not installed
// Normally we just need to include the "dbghelp.h" file
#include <imagehlp.h>
#if API_VERSION_NUMBER < 9
typedef
BOOL
(__stdcall *PREAD_PROCESS_MEMORY_ROUTINE64)(
HANDLE hProcess,
DWORD64 qwBaseAddress,
PVOID lpBuffer,
DWORD nSize,
LPDWORD lpNumberOfBytesRead
);
typedef struct _IMAGEHLP_LINE64 {
DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_LINE64)
PVOID Key; // internal
DWORD LineNumber; // line number in file
PCHAR FileName; // full filename
DWORD64 Address; // first instruction of line
} IMAGEHLP_LINE64, *PIMAGEHLP_LINE64;
typedef struct _IMAGEHLP_MODULE64 {
DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_MODULE64)
DWORD64 BaseOfImage; // base load address of module
DWORD ImageSize; // virtual size of the loaded module
DWORD TimeDateStamp; // date/time stamp from pe header
DWORD CheckSum; // checksum from the pe header
DWORD NumSyms; // number of symbols in the symbol table
SYM_TYPE SymType; // type of symbols loaded
CHAR ModuleName[32]; // module name
CHAR ImageName[256]; // image name
CHAR LoadedImageName[256]; // symbol file name
} IMAGEHLP_MODULE64, *PIMAGEHLP_MODULE64;
typedef struct _IMAGEHLP_SYMBOL64 {
DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_SYMBOL64)
DWORD64 Address; // virtual address including dll base address
DWORD Size; // estimated size of symbol, can be zero
DWORD Flags; // info about the symbols, see the SYMF defines
DWORD MaxNameLength; // maximum size of symbol name in 'Name'
CHAR Name[1]; // symbol name (null terminated string)
} IMAGEHLP_SYMBOL64, *PIMAGEHLP_SYMBOL64;
typedef struct _tagADDRESS64 {
DWORD64 Offset;
WORD Segment;
ADDRESS_MODE Mode;
} ADDRESS64, *LPADDRESS64;
typedef struct _KDHELP64 {
//
// address of kernel thread object, as provided in the
// WAIT_STATE_CHANGE packet.
//
DWORD64 Thread;
//
// offset in thread object to pointer to the current callback frame
// in kernel stack.
//
DWORD ThCallbackStack;
//
// offset in thread object to pointer to the current callback backing
// store frame in kernel stack.
//
DWORD ThCallbackBStore;
//
// offsets to values in frame:
//
// address of next callback frame
DWORD NextCallback;
// address of saved frame pointer (if applicable)
DWORD FramePointer;
//
// Address of the kernel function that calls out to user mode
//
DWORD64 KiCallUserMode;
//
// Address of the user mode dispatcher function
//
DWORD64 KeUserCallbackDispatcher;
//
// Lowest kernel mode address
//
DWORD64 SystemRangeStart;
DWORD64 Reserved[8];
} KDHELP64, *PKDHELP64;
typedef struct _tagSTACKFRAME64 {
ADDRESS64 AddrPC; // program counter
ADDRESS64 AddrReturn; // return address
ADDRESS64 AddrFrame; // frame pointer
ADDRESS64 AddrStack; // stack pointer
ADDRESS64 AddrBStore; // backing store pointer
PVOID FuncTableEntry; // pointer to pdata/fpo or NULL
DWORD64 Params[4]; // possible arguments to the function
BOOL Far; // WOW far call
BOOL Virtual; // is this a virtual frame?
DWORD64 Reserved[3];
KDHELP64 KdHelp;
} STACKFRAME64, *LPSTACKFRAME64;
typedef
PVOID
(__stdcall *PFUNCTION_TABLE_ACCESS_ROUTINE64)(
HANDLE hProcess,
DWORD64 AddrBase
);
typedef
DWORD64
(__stdcall *PGET_MODULE_BASE_ROUTINE64)(
HANDLE hProcess,
DWORD64 Address
);
typedef
DWORD64
(__stdcall *PTRANSLATE_ADDRESS_ROUTINE64)(
HANDLE hProcess,
HANDLE hThread,
LPADDRESS64 lpaddr
);
#endif
// #############################################################################################
// Forward definitions of functions:
static void ShowStackRM( HANDLE hThread, CONTEXT& c, FILE *fLogFile, PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryFunction, HANDLE hProcess);
static void ShowStack( HANDLE hThread, CONTEXT& c, FILE *fLogFile);
#ifndef HASH_ENTRY_REMOVE_AT_FREE
static void AllocHashOut(FILE*);
#endif
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? 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? EIP
DWORD dwEIPOffset;
DWORD dwEIPLen;
char pcEIPAddr[MAX_EIP_LEN_BUF];
// Callstack f? 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? 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?e
// wenn dies oft vorkommt, sollte man entweder die Tabelle vergrößern oder eine
// andere Hash-Funktion w?len
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?kgegeben, so wurde der Key nicht
// gefunden, ansonsten wird ein Zeiger auf den Hash-Eintrag zur?kgegeben
// ACHTUNG: In einem preemptiven Tasking-System kann hier nicht
// garantiert werden, ob der Zeiger noch g?tig ist, wenn er
// zur?kgegeben wird, da er von einem anderen Thread schon
// freigegeben sein k?nte.
// Die synchronisation mu?eine Ebene h?er 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?che also die Tabelle
memset(&IMallocHashTable[HashIdx], 0, sizeof(IMallocHashTable[HashIdx]));
}
else {
// Es sind noch Eintr?e verkettet, ?erschreibe 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?le also wieder zur?k:
IMallocHashCurrentCollisions--;
pHashEntryLast->Next = pHashEntry->Next;
_free_dbg(pHashEntry, _CRT_BLOCK);
return TRUE;
}
#else
// erh?e 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? meine CallStack-Ausgabe aus der Hash-Tabelle
static BOOL __stdcall ReadProcMemoryFromIMallocHash(HANDLE pData, DWORD64 lpBaseAddress, PVOID lpBuffer, DWORD nSize, PDWORD lpNumberOfBytesRead) {
// Versuche die hRequestID zu finden
IMallocHashEntryType *pHashEntry;
*lpNumberOfBytesRead = 0;
pHashEntry = IMallocHashFind((PVOID) pData);
if (pHashEntry == (IMallocHashEntryType*) ALLOC_ENTRY_NOT_FOUND) {
// nicht gefunden, somit kann ich den Speicher nicht lesen
*lpNumberOfBytesRead = 0;
return FALSE;
}
if ( ((DWORD) lpBaseAddress >= pHashEntry->dwESPOffset) && ((DWORD) lpBaseAddress <= (pHashEntry->dwESPOffset+pHashEntry->dwESPLen)) ) {
// Speicher liegt im ESP:
// Errechne den Offset
DWORD dwOffset = (DWORD) lpBaseAddress - pHashEntry->dwESPOffset;
DWORD dwSize = __min(nSize, MAX_ESP_LEN_BUF-dwOffset);
memcpy(lpBuffer, &(pHashEntry->pcESPAddr[dwOffset]), dwSize);
*lpNumberOfBytesRead = dwSize;
if (dwSize != nSize)
return FALSE;
}
if ( ((DWORD) lpBaseAddress >= pHashEntry->dwEIPOffset) && ((DWORD) lpBaseAddress <= (pHashEntry->dwEIPOffset+pHashEntry->dwEIPLen)) ) {
// Speicher liegt im EIP:
// Errechne den Offset
DWORD dwOffset = (DWORD) lpBaseAddress - pHashEntry->dwEIPOffset;
DWORD dwSize = __min(nSize, MAX_ESP_LEN_BUF-dwOffset);
memcpy(lpBuffer, &(pHashEntry->pcEIPAddr[dwOffset]), dwSize);
*lpNumberOfBytesRead = dwSize;
if (dwSize != nSize)
return FALSE;
}
if (*lpNumberOfBytesRead == 0) // Der Speicher konnte nicht gefunden werden
return FALSE;
return TRUE;
}
// AllocHashOutLeaks
// Gibt allen Speicher aus, der noch nicht wieder freigegeben wurde
// Returns the number of bytes, that are not freed (leaks)
ULONG IMallocHashOutLeaks(FILE *fFile) {
ULONG ulTemp;
IMallocHashEntryType *pHashEntry;
ULONG ulCount = 0;
ULONG ulLeaksByte = 0;
// Gehe jeden Eintrag durch und gebe ihn aus
for(ulTemp = 0; ulTemp < ALLOC_HASH_ENTRIES; ulTemp++) {
pHashEntry = &IMallocHashTable[ulTemp];
if (pHashEntry->pData != 0) {
while(pHashEntry != NULL) {
// gebe die Zeile aus
if ( (pHashEntry->cRemovedFlag <= 0) || (pHashEntry->cRemovedFlag > 1) ) {
ulCount++;
if (g_CallstackOutputType == ACOutput_XML)
_ftprintf(fFile, _T("<LEAK requestID=\"%u\" size=\"%u\">\n"), pHashEntry->pData, pHashEntry->nDataSize);
else
_ftprintf(fFile, _T("Pointer (RequestID): %12i, Removed: %i, Size: %12i\n"), pHashEntry->pData, pHashEntry->cRemovedFlag, pHashEntry->nDataSize);
CONTEXT c;
memset( &c, '\0', sizeof c );
c.Eip = pHashEntry->dwEIPOffset;
c.Ebp = pHashEntry->dwESPOffset;
ShowStackRM( NULL, c, fFile, &ReadProcMemoryFromIMallocHash, (HANDLE) pHashEntry->pData);
// Z?le 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? diesen zumindest 1 Byte
if (g_CallstackOutputType == ACOutput_XML)
_ftprintf(fFile, _T("</LEAK>\n")); // terminate the xml-node
}
pHashEntry = pHashEntry->Next;
}
}
}
if (g_CallstackOutputType != ACOutput_XML)
_ftprintf(fFile, _T("\n**** Number of leaks: %i\n"), ulCount);
return ulLeaksByte;
} // AllocHashOutLeaks
#endif
static void AllocHashInit(void) {
memset(AllocHashTable, 0, sizeof(AllocHashTable));
AllocHashEntries = 0;
AllocHashCollisions = 0;
AllocHashFreed = 0;
AllocHashCurrentCount = 0;
AllocHashMaxUsed = 0;
AllocHashMaxCollisions = 0;
AllocHashCurrentCollisions = 0;
#ifdef WITH_IMALLOC_SPY
memset(IMallocHashTable, 0, sizeof(IMallocHashTable));
IMallocHashEntries = 0;
IMallocHashCollisions = 0;
IMallocHashFreed = 0;
IMallocHashCurrentCount = 0;
IMallocHashMaxUsed = 0;
IMallocHashMaxCollisions = 0;
IMallocHashCurrentCollisions = 0;
#endif
return;
} // AllocHashInit
// AllocHashDeinit
// Returns the number of bytes, that are not freed (leaks)
static ULONG AllocHashDeinit(void) {
ULONG ulRet = 0;
BOOL bAppend = g_CallstackOutputType != ACOutput_XML;
AllocCheckFileOpen(bAppend); // open global log-file
if (g_CallstackOutputType == ACOutput_XML)
{
_ftprintf(g_fFile, _T("<MEMREPORT "));
WriteDateTime(g_fFile, TRUE);
_ftprintf(g_fFile, _T(">\n"));
}
else
{
_ftprintf(g_fFile, _T("\n##### Memory Report ########################################\n"));
WriteDateTime(g_fFile);
_ftprintf(g_fFile, _T("\n"));
}
#ifndef HASH_ENTRY_REMOVE_AT_FREE
// output the used memory
if (g_CallstackOutputType != ACOutput_XML)
_ftprintf(g_fFile, _T("##### Memory used: #########################################\n"));
AllocHashOut(g_fFile);
#endif
// output the Memoty leaks
if (g_CallstackOutputType != ACOutput_XML)
_ftprintf(g_fFile, _T("\n##### Leaks: ###############################################\n"));
ulRet = AllocHashOutLeaks(g_fFile);
if (g_CallstackOutputType == ACOutput_Advanced)
{
// output some statistics from the hash-table
_ftprintf(g_fFile, _T("***** Hash-Table statistics:\n"));
_ftprintf(g_fFile, _T(" Table-Size: %i\n"), ALLOC_HASH_ENTRIES);
_ftprintf(g_fFile, _T(" Inserts: %i\n"), AllocHashEntries);
_ftprintf(g_fFile, _T(" Freed: %i\n"), AllocHashFreed);
_ftprintf(g_fFile, _T(" Sum Collisions: %i\n"), AllocHashCollisions);
_ftprintf(g_fFile, _T("\n"));
_ftprintf(g_fFile, _T(" Max used: %i\n"), AllocHashMaxUsed);
_ftprintf(g_fFile, _T(" Max Collisions: %i\n"), AllocHashMaxCollisions);
}
// Free Hash-Table
ULONG ulTemp;
AllocHashEntryType *pHashEntry = NULL;
AllocHashEntryType *pHashEntryOld = NULL;
// 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?che die gesamte Hash-Tabelle
memset(IMallocHashTable, 0, sizeof(IMallocHashTable));
#endif
if (g_CallstackOutputType == ACOutput_XML)
_ftprintf(g_fFile, _T("</MEMREPORT>\n"));
return ulRet;
} // AllocHashDeinit
// AllocHashFunction
// The has-function (very simple)
static inline ULONG AllocHashFunction(long lRequestID) {
// I couldn´t find any better and faster
return lRequestID % ALLOC_HASH_ENTRIES;
} // AllocHashFunction
// AllocHashInsert
// lRequestID: Key-Word (RequestID from AllocHook)
// pContext: Context-Record, for retrieving Callstack (EIP and EBP is only needed)
// nDataSize: How many bytes
static void AllocHashInsert(long lRequestID, CONTEXT &Context, size_t nDataSize) {
ULONG HashIdx;
AllocHashEntryType *pHashEntry;
// change statistical data
AllocHashEntries++;
AllocHashCurrentCount++;
if (AllocHashCurrentCount > AllocHashMaxUsed)
AllocHashMaxUsed = AllocHashCurrentCount;
// generate hash-value
HashIdx = AllocHashFunction(lRequestID);
pHashEntry = &AllocHashTable[HashIdx];
if (pHashEntry->lRequestID == 0) {
// Entry is empty...
}
else {
// Entry is not empy! make a list of entries for this hash value...
// change statistical data
// if this happens often, you should increase the hah size or change the heash-function;
// to fasten the allocation time
AllocHashCollisions++;
AllocHashCurrentCollisions++;
if (AllocHashCurrentCollisions > AllocHashMaxCollisions)
AllocHashMaxCollisions = AllocHashCurrentCollisions;
while(pHashEntry->Next != NULL) {
pHashEntry = pHashEntry->Next;
}
pHashEntry->Next = (AllocHashEntryType*) calloc(sizeof(AllocHashEntryType), 1);
pHashEntry = pHashEntry->Next;
}
pHashEntry->lRequestID = lRequestID; // Key-Word
pHashEntry->nDataSize = nDataSize;
pHashEntry->Next = NULL;
// Get EIP and save it in the record
pHashEntry->dwEIPOffset = Context.Eip;
if (ReadProcessMemory(GetCurrentProcess(), (LPCVOID) Context.Eip, &(pHashEntry->pcEIPAddr), MAX_EIP_LEN_BUF, &(pHashEntry->dwEIPLen)) == 0) {
// Could not read memory... remove everything...
memset(pHashEntry->pcEIPAddr, 0, MAX_EIP_LEN_BUF);
pHashEntry->dwEIPLen = 0;
pHashEntry->dwEIPOffset = 0;
}
// Get ESP and save it in the record
pHashEntry->dwESPOffset = Context.Ebp;
if (ReadProcessMemory(GetCurrentProcess(), (LPCVOID) Context.Ebp, &(pHashEntry->pcESPAddr), MAX_ESP_LEN_BUF, &(pHashEntry->dwESPLen)) == 0) {
// Could not read memory... remove everything...
memset(pHashEntry->pcESPAddr, 0, MAX_ESP_LEN_BUF);
pHashEntry->dwESPLen = 0;
pHashEntry->dwESPOffset = 0;
// Check if I tried to read too much...
if (GetLastError() == ERROR_PARTIAL_COPY)
{
// ask how many I can read:
MEMORY_BASIC_INFORMATION MemBuffer;
DWORD dwRet = VirtualQuery((LPCVOID) Context.Ebp, &MemBuffer, sizeof(MemBuffer));
if (dwRet > 0)
{
// calculate the length
DWORD len = ((DWORD) MemBuffer.BaseAddress + MemBuffer.RegionSize) - Context.Ebp;
if ( (len > 0) && (len < MAX_ESP_LEN_BUF) )
{
// try to read it again (with the shorter length)
if (ReadProcessMemory(GetCurrentProcess(), (LPCVOID) Context.Ebp, &(pHashEntry->pcESPAddr), len, &(pHashEntry->dwESPLen)) == 0)
{
// ok, now everything goes wrong... remove it...
memset(pHashEntry->pcESPAddr, 0, MAX_ESP_LEN_BUF);
pHashEntry->dwESPLen = 0;
pHashEntry->dwESPOffset = 0;
}
else
{
pHashEntry->dwESPOffset = Context.Ebp;
}
}
} // VirtualQuery was successfully
} // ERROR_PARTIAL_COPY
}
}
// AllocHashFind
// If ALLOC_ENTRY_NOT_FOUND is returned, the Key was not found!
// If the Key was found, a pointer to the entry is returned
static AllocHashEntryType *AllocHashFind(long lRequestID) {
ULONG HashIdx;
AllocHashEntryType *pHashEntry;
// get the Hash-Value
HashIdx = AllocHashFunction(lRequestID);
// Just do some simple checks:
_ASSERTE(HashIdx < ALLOC_HASH_ENTRIES);
pHashEntry = &AllocHashTable[HashIdx];
while(pHashEntry != NULL) {
if (pHashEntry->lRequestID == lRequestID) {
return pHashEntry;
}
pHashEntry = pHashEntry->Next;
}
// entry was not found!
return (AllocHashEntryType*) ALLOC_ENTRY_NOT_FOUND;
} // AllocHashFind
// AllocHashRemove
// Return: FALSE (0) : Key was found and removed/marked
// TRUE (!=0): Key was not found
static BOOL AllocHashRemove(long lRequestID) {
ULONG HashIdx;
AllocHashEntryType *pHashEntry, *pHashEntryLast;
// get the Hash-Value
HashIdx = AllocHashFunction(lRequestID);
// Just do some simple checks:
_ASSERTE(HashIdx < ALLOC_HASH_ENTRIES);
pHashEntryLast = NULL;
pHashEntry = &AllocHashTable[HashIdx];
while(pHashEntry != NULL) {
if (pHashEntry->lRequestID == lRequestID) {
#ifdef HASH_ENTRY_REMOVE_AT_FREE
AllocHashFreed++;
AllocHashCurrentCount--;
// release my memory
if (pHashEntryLast == NULL) {
// It is an entry in the table, so do not release this memory
if (pHashEntry->Next == NULL) {
// It was the last entry, so empty the table entry
memset(&AllocHashTable[HashIdx], 0, sizeof(AllocHashTable[HashIdx]));
}
else {
// There are some more entries, so shorten the list
AllocHashEntryType *pTmp = pHashEntry->Next;
*pHashEntry = *(pHashEntry->Next);
free(pTmp);
}
return TRUE;
}
else {
// now, I am in an dynamic allocated entry
// it was a collision, so decrease the current collision count
AllocHashCurrentCollisions--;
pHashEntryLast->Next = pHashEntry->Next;
free(pHashEntry);
return TRUE;
}
#else
// increase the Remove-Count and let the objet stay in memory
pHashEntry->cRemovedFlag++;
return TRUE;
#endif
}
pHashEntryLast = pHashEntry;
pHashEntry = pHashEntry->Next;
}
// if we are here, we could not find the RequestID
return FALSE;
}
// ReadProcMemoryFromHash
// Callback-Funtion for StackWalk for my own CallStack from the Hash-Table-Entries
static BOOL __stdcall ReadProcMemoryFromHash(HANDLE hRequestID, DWORD64 lpBaseAddress, PVOID lpBuffer, DWORD nSize, PDWORD lpNumberOfBytesRead) {
// Try to find the RequestID
AllocHashEntryType *pHashEntry;
*lpNumberOfBytesRead = 0;
pHashEntry = AllocHashFind((LONG) hRequestID);
if (pHashEntry == (AllocHashEntryType*) ALLOC_ENTRY_NOT_FOUND) {
// Not found, so I cannot return any memory
*lpNumberOfBytesRead = 0;
return FALSE;
}
if ( ((DWORD) lpBaseAddress >= pHashEntry->dwESPOffset) && ((DWORD) lpBaseAddress <= (pHashEntry->dwESPOffset+pHashEntry->dwESPLen)) ) {
// Memory is located in ESP:
// Calculate the offset
DWORD dwOffset = (DWORD) lpBaseAddress - pHashEntry->dwESPOffset;
DWORD dwSize = __min(nSize, MAX_ESP_LEN_BUF-dwOffset);
memcpy(lpBuffer, &(pHashEntry->pcESPAddr[dwOffset]), dwSize);
*lpNumberOfBytesRead = dwSize;
if (dwSize != nSize)
return FALSE;
}
if ( ((DWORD) lpBaseAddress >= pHashEntry->dwEIPOffset) && ((DWORD) lpBaseAddress <= (pHashEntry->dwEIPOffset+pHashEntry->dwEIPLen)) ) {
// Memory is located in EIP:
// Calculate the offset
DWORD dwOffset = (DWORD) lpBaseAddress - pHashEntry->dwEIPOffset;
DWORD dwSize = __min(nSize, MAX_ESP_LEN_BUF-dwOffset);
memcpy(lpBuffer, &(pHashEntry->pcEIPAddr[dwOffset]), dwSize);
*lpNumberOfBytesRead = dwSize;
if (dwSize != nSize)
return FALSE;
}
if (*lpNumberOfBytesRead == 0) // Memory could not be found
return FALSE;
return TRUE;
}
// AllocHashOutLeaks
// Write all Memory (with callstack) which was not freed yet
// Returns the number of bytes, that are not freed (leaks)
ULONG AllocHashOutLeaks(FILE *fFile) {
ULONG ulTemp;
AllocHashEntryType *pHashEntry;
ULONG ulCount = 0;
ULONG ulLeaksByte = 0;
// Move throu every entry
for(ulTemp = 0; ulTemp < ALLOC_HASH_ENTRIES; ulTemp++) {
pHashEntry = &AllocHashTable[ulTemp];
if (pHashEntry->lRequestID != 0) {
while(pHashEntry != NULL) {
if ( (pHashEntry->cRemovedFlag <= 0) || (pHashEntry->cRemovedFlag > 1) ) {
ulCount++;
if (g_CallstackOutputType == ACOutput_XML)
_ftprintf(fFile, _T("<LEAK requestID=\"%u\" size=\"%u\">\n"), pHashEntry->lRequestID, pHashEntry->nDataSize);
else
_ftprintf(fFile, _T("RequestID: %12i, Removed: %i, Size: %12i\n"), pHashEntry->lRequestID, pHashEntry->cRemovedFlag, pHashEntry->nDataSize);
CONTEXT c;
memset( &c, '\0', sizeof c );
c.Eip = pHashEntry->dwEIPOffset;
c.Ebp = pHashEntry->dwESPOffset;
ShowStackRM( NULL, c, fFile, &ReadProcMemoryFromHash, (HANDLE) pHashEntry->lRequestID);
// Count the number of leaky bytes
if (pHashEntry->nDataSize > 0)
ulLeaksByte += pHashEntry->nDataSize;
else
ulLeaksByte++; // If memory was allocated with zero bytes, then just increase the counter 1
if (g_CallstackOutputType == ACOutput_XML)
_ftprintf(fFile, _T("</LEAK>\n")); // terminate the xml-node
}
pHashEntry = pHashEntry->Next;
}
}
}
if (g_CallstackOutputType != ACOutput_XML)
_ftprintf(fFile, _T("\n**** Number of leaks: %i\n"), ulCount);
return ulLeaksByte;
} // AllocHashOutLeaks
// Write all used memory to a file
void AllocHashOut(FILE *fFile) {
ULONG ulTemp;
AllocHashEntryType *pHashEntry;
for(ulTemp = 0; ulTemp < ALLOC_HASH_ENTRIES; ulTemp++) {
pHashEntry = &AllocHashTable[ulTemp];
if (pHashEntry->lRequestID != 0) {
while(pHashEntry != NULL) {
if (g_CallstackOutputType == ACOutput_XML)
_ftprintf(fFile, _T("<MEMUSED requestID=\"%u\" size=\"%u\"\n"), pHashEntry->lRequestID, pHashEntry->nDataSize);
else
_ftprintf(fFile, _T("RequestID: %12i, Removed: %i, Size: %12i\n"), pHashEntry->lRequestID, pHashEntry->cRemovedFlag, pHashEntry->nDataSize);
pHashEntry = pHashEntry->Next;
}
}
}
} // AllocHashOut
/*******************************************************************************
* Ende der Hash-Tabelle
*******************************************************************************/
// The follwoing is copied from dbgint.h:
// <CRT_INTERNALS>
/*
* For diagnostic purpose, blocks are allocated with extra information and
* stored in a doubly-linked list. This makes all blocks registered with
* how big they are, when they were allocated, and what they are used for.
*/
#define nNoMansLandSize 4
typedef struct _CrtMemBlockHeader
{
struct _CrtMemBlockHeader * pBlockHeaderNext;
struct _CrtMemBlockHeader * pBlockHeaderPrev;
char * szFileName;
int nLine;
#ifdef _WIN64
/* These items are reversed on Win64 to eliminate gaps in the struct
* and ensure that sizeof(struct)%16 == 0, so 16-byte alignment is
* maintained in the debug heap.
*/
int nBlockUse;
size_t nDataSize;
#else /* _WIN64 */
size_t nDataSize;
int nBlockUse;
#endif /* _WIN64 */
long lRequest;
unsigned char gap[nNoMansLandSize];
/* followed by:
* unsigned char data[nDataSize];
* unsigned char anotherGap[nNoMansLandSize];
*/
} _CrtMemBlockHeader;
#define pbData(pblock) ((unsigned char *)((_CrtMemBlockHeader *)pblock + 1))
#define pHdr(pbData) (((_CrtMemBlockHeader *)pbData)-1)
// </CRT_INTERNALS>
// Global data:
static BOOL g_bInitialized = FALSE;
static HINSTANCE g_hImagehlpDll = NULL;
static DWORD g_dwShowCount = 0; // increase at every ShowStack-Call
static CRITICAL_SECTION g_csFileOpenClose = {0};
// Is used for syncronising call to MyAllocHook (to prevent reentrant calls)
static LONG g_lMallocCalled = 0;
static _CRT_ALLOC_HOOK pfnOldCrtAllocHook = NULL;
// Deaktivate AllocHook, by increasing the Syncronisation-Counter
/*
static void DeactivateMallocStackwalker(void) {
InterlockedIncrement(&g_lMallocCalled);
}
*/
// MyAllocHook is Single-Threaded, that means the the calls are serialized in the calling function!
// Special case for VC 5
#if _MSC_VER <= 1100
static int MyAllocHook(int nAllocType, void *pvData,
size_t nSize, int nBlockUse, long lRequest,
const char * szFileName, int nLine ) {
#else
static int MyAllocHook(int nAllocType, void *pvData,
size_t nSize, int nBlockUse, long lRequest,
const unsigned char * szFileName, int nLine ) {
#endif
static TCHAR *operation[] = { _T(""), _T("ALLOCATIONG"), _T("RE-ALLOCATING"), _T("FREEING") };
static TCHAR *blockType[] = { _T("Free"), _T("Normal"), _T("CRT"), _T("Ignore"), _T("Client") };
#ifdef IGNORE_CRT_ALLOC
if (_BLOCK_TYPE(nBlockUse) == _CRT_BLOCK) // Ignore internal C runtime library allocations
return TRUE;
#endif
extern int _crtDbgFlag;
if ( ((_CRTDBG_ALLOC_MEM_DF & _crtDbgFlag) == 0) && ( (nAllocType == _HOOK_ALLOC) || (nAllocType == _HOOK_REALLOC) ) )
{
// Someone has disabled that the runtime should log this allocation
// so we do not log this allocation
if (pfnOldCrtAllocHook != NULL)
pfnOldCrtAllocHook(nAllocType, pvData, nSize, nBlockUse, lRequest, szFileName, nLine);
return TRUE;
}
// Prevent from reentrat calls
if (InterlockedIncrement(&g_lMallocCalled) > 1) { // I was already called
InterlockedDecrement(&g_lMallocCalled);
// call the previous alloc hook
if (pfnOldCrtAllocHook != NULL)
pfnOldCrtAllocHook(nAllocType, pvData, nSize, nBlockUse, lRequest, szFileName, nLine);
return TRUE;
}
if (g_ulShowStackAtAlloc > 0) {
AllocCheckFileOpen(); // Open logfile
}
_ASSERT( (nAllocType == _HOOK_ALLOC) || (nAllocType == _HOOK_REALLOC) || (nAllocType == _HOOK_FREE) );
_ASSERT( ( _BLOCK_TYPE(nBlockUse) >= 0 ) && ( _BLOCK_TYPE(nBlockUse) < 5 ) );
if (nAllocType == _HOOK_FREE) { // freeing
// Try to get the header information
if (_CrtIsValidHeapPointer(pvData)) { // it is a valid Heap-Pointer
// get the ID
_CrtMemBlockHeader *pHead;
// get a pointer to memory block header
pHead = pHdr(pvData);
nSize = pHead->nDataSize;
lRequest = pHead->lRequest; // This is the ID!
if (pHead->nBlockUse == _IGNORE_BLOCK)
{
InterlockedDecrement(&g_lMallocCalled);
if (pfnOldCrtAllocHook != NULL)
pfnOldCrtAllocHook(nAllocType, pvData, nSize, nBlockUse, lRequest, szFileName, nLine);
return TRUE;
}
}
}
if (g_ulShowStackAtAlloc > 0) {
_ftprintf( g_fFile, _T("##### Memory operation: %s a %d-byte '%s' block (# %ld)"),
operation[nAllocType], nSize, blockType[_BLOCK_TYPE(nBlockUse)], lRequest );
if ( pvData != NULL )
_ftprintf( g_fFile, _T(" at 0x%X"), pvData );
_ftprintf(g_fFile, _T("\n"));
}
if (nAllocType == _HOOK_FREE) { // freeing:
if (lRequest != 0) { // RequestID was found
BOOL bRet;
// Try to find the RequestID in the Hash-Table, mark it that it was freed
bRet = AllocHashRemove(lRequest);
if(g_ulShowStackAtAlloc > 0) {
if (bRet == FALSE) {
// RequestID not found!
_ftprintf(g_fFile, _T("###### RequestID not found in hash table for FREEING (%i)!\n"), lRequest);
}
} // g_ulShowStackAtAlloc > 0
}
else {
if(g_ulShowStackAtAlloc > 0) {
// No valid RequestID found, display error
_ftprintf(g_fFile, _T("###### No valid RequestID for FREEING! (0x%X)\n"), pvData);
}
}
} // freeing
if (nAllocType == _HOOK_REALLOC) { // re-allocating
// Try to get the header information
if (_CrtIsValidHeapPointer(pvData)) { // it is a valid Heap-Pointer
BOOL bRet;
LONG lReallocRequest;
// get the ID
_CrtMemBlockHeader *pHead;
// get a pointer to memory block header
pHead = pHdr(pvData);
// Try to find the RequestID in the Hash-Table, mark it that it was freed
lReallocRequest = pHead->lRequest;
bRet = AllocHashRemove(lReallocRequest);
if (g_ulShowStackAtAlloc > 0) {
if (bRet == FALSE) {
// RequestID not found!
_ftprintf(g_fFile, _T("###### RequestID not found in hash table for RE-ALLOCATING (%i)!\n"), lReallocRequest);
}
else {
_ftprintf(g_fFile, _T("##### Implicit freeing because of re-allocation (# old: %ld, new: %ld)\n"), lReallocRequest, lRequest);
}
} // g_ulShowStackAtAlloc > 0
} // ValidHeapPointer
} // re-allocating
if ( (g_ulShowStackAtAlloc < 3) && (nAllocType == _HOOK_FREE) ) {
InterlockedDecrement(&g_lMallocCalled);
// call the previous alloc hook
if (pfnOldCrtAllocHook != NULL)
pfnOldCrtAllocHook(nAllocType, pvData, nSize, nBlockUse, lRequest, szFileName, nLine);
return TRUE;
}
// Get the context DIESES of this Thread
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;
// init CONTEXT record so we know where to start the stackwalk
if ( GetThreadContext( 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
}
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 = NULL;
tCT32S pCT32S = NULL;
tM32F pM32F = NULL;
tM32N pM32N = NULL;
HANDLE hSnap = 0;
MODULEENTRY32 me;
me.dwSize = sizeof(me);
BOOL keepGoing;
ModuleEntry e;
int i;
for (i = 0; i<lenof(dllname); i++ )
{
hToolhelp = LoadLibrary( dllname[i] );
if (hToolhelp == NULL)
continue;
pCT32S = (tCT32S) GetProcAddress(hToolhelp, "CreateToolhelp32Snapshot");
pM32F = (tM32F) GetProcAddress(hToolhelp, "Module32First");
pM32N = (tM32N) GetProcAddress(hToolhelp, "Module32Next");
if ( pCT32S != 0 && pM32F != 0 && pM32N != 0 )
break; // found the functions!
FreeLibrary(hToolhelp);
hToolhelp = NULL;
}
if (hToolhelp == NULL)
return false;
hSnap = pCT32S( TH32CS_SNAPMODULE, pid );
if (hSnap == (HANDLE) -1)
return false;
keepGoing = !!pM32F( hSnap, &me );
while (keepGoing)
{
e.imageName = me.szExePath;
e.moduleName = me.szModule;
e.baseAddress = (DWORD) me.modBaseAddr;
e.size = me.modBaseSize;
modules.push_back( e );
keepGoing = !!pM32N( hSnap, &me );
}
CloseHandle(hSnap);
FreeLibrary(hToolhelp);
return modules.size() != 0;
} // GetModuleListTH32
// **************************************** PSAPI ************************
typedef struct _MODULEINFO {
LPVOID lpBaseOfDll;
DWORD SizeOfImage;
LPVOID EntryPoint;
} MODULEINFO, *LPMODULEINFO;
static BOOL GetModuleListPSAPI(ModuleList &modules, DWORD pid, HANDLE hProcess, FILE *fLogFile)
{
// EnumProcessModules()
typedef BOOL (__stdcall *tEPM)(HANDLE hProcess, HMODULE *lphModule, DWORD cb, LPDWORD lpcbNeeded );
// GetModuleFileNameEx()
typedef DWORD (__stdcall *tGMFNE)(HANDLE hProcess, HMODULE hModule, LPSTR lpFilename, DWORD nSize );
// GetModuleBaseName()
typedef DWORD (__stdcall *tGMBN)(HANDLE hProcess, HMODULE hModule, LPSTR lpFilename, DWORD nSize );
// GetModuleInformation()
typedef BOOL (__stdcall *tGMI)(HANDLE hProcess, HMODULE hModule, LPMODULEINFO pmi, DWORD nSize );
HINSTANCE hPsapi;
tEPM pEPM;
tGMFNE pGMFNE;
tGMBN pGMBN;
tGMI pGMI;
DWORD i;
ModuleEntry e;
DWORD cbNeeded;
MODULEINFO mi;
HMODULE *hMods = 0;
char *tt = 0;
hPsapi = LoadLibrary( _T("psapi.dll") );
if ( hPsapi == 0 )
return false;
modules.clear();
pEPM = (tEPM) GetProcAddress( hPsapi, "EnumProcessModules" );
pGMFNE = (tGMFNE) GetProcAddress( hPsapi, "GetModuleFileNameExA" );
pGMBN = (tGMFNE) GetProcAddress( hPsapi, "GetModuleBaseNameA" );
pGMI = (tGMI) GetProcAddress( hPsapi, "GetModuleInformation" );
if ( pEPM == 0 || pGMFNE == 0 || pGMBN == 0 || pGMI == 0 )
{
// we couldn´t find all functions
FreeLibrary( hPsapi );
return false;
}
hMods = (HMODULE*) malloc(sizeof(HMODULE) * (TTBUFLEN / sizeof HMODULE));
tt = (char*) malloc(sizeof(char) * TTBUFLEN);
if ( ! pEPM( hProcess, hMods, TTBUFLEN, &cbNeeded ) )
{
_ftprintf(fLogFile, _T("%lu: EPM failed, GetLastError = %lu\n"), g_dwShowCount, gle );
goto cleanup;
}
if ( cbNeeded > TTBUFLEN )
{
_ftprintf(fLogFile, _T("%lu: More than %lu module handles. Huh?\n"), g_dwShowCount, lenof( hMods ) );
goto cleanup;
}
for ( i = 0; i < cbNeeded / sizeof hMods[0]; i++ )
{
// base address, size
pGMI(hProcess, hMods[i], &mi, sizeof mi );
e.baseAddress = (DWORD) mi.lpBaseOfDll;
e.size = mi.SizeOfImage;
// image file name
tt[0] = 0;
pGMFNE(hProcess, hMods[i], tt, TTBUFLEN );
e.imageName = tt;
// module name
tt[0] = 0;
pGMBN(hProcess, hMods[i], tt, TTBUFLEN );
e.moduleName = tt;
modules.push_back(e);
}
cleanup:
if (hPsapi)
FreeLibrary(hPsapi);
free(tt);
free(hMods);
return modules.size() != 0;
} // GetModuleListPSAPI
static BOOL GetModuleList(ModuleList& modules, DWORD pid, HANDLE hProcess, FILE *fLogFile)
{
// first try toolhelp32
if (GetModuleListTH32(modules, pid, fLogFile) )
return true;
// then try psapi
return GetModuleListPSAPI(modules, pid, hProcess, fLogFile);
} // GetModuleList
static void EnumAndLoadModuleSymbols( HANDLE hProcess, DWORD pid, FILE *fLogFile )
{
static ModuleList modules;
static ModuleListIter it;
char *img, *mod;
// fill in module list
GetModuleList(modules, pid, hProcess, fLogFile);
for ( it = modules.begin(); it != modules.end(); ++ it )
{
// SymLoadModule() wants writeable strings
img = strdup(it->imageName.c_str());
mod = strdup(it->moduleName.c_str());
pSLM( hProcess, 0, img, mod, it->baseAddress, it->size );
free(img);
free(mod);
std::string s;
}
} // EnumAndLoadModuleSymbols
static int InitStackWalk(void)
{
if (g_bInitialized != FALSE)
return 0; // already initialized
// 02-12-19: Now we only support dbghelp.dll!
// To use it on NT you have to install the redistrubutable for DBGHELP.DLL
g_hImagehlpDll = LoadLibrary( _T("dbghelp.dll") );
if ( g_hImagehlpDll == NULL )
{
printf( "LoadLibrary( \"dbghelp.dll\" ): GetLastError = %lu\n", gle );
g_bInitialized = FALSE;
return 1;
}
// now we only support the newer dbghlp.dll with the "64"-functions (StackWalk64, a.s.o.)
// If your dbghlp.dll does not support this, please download the redistributable from MS
// Normally from: http://www.microsoft.com/downloads/details.aspx?displaylang=en&FamilyID=CD1FC4B2-0885-47F4-AF45-7FD5E14DB6C0
pSC = (tSC) GetProcAddress( g_hImagehlpDll, "SymCleanup" );
pSFTA = (tSFTA) GetProcAddress( g_hImagehlpDll, "SymFunctionTableAccess64" );
pSGLFA = (tSGLFA) GetProcAddress( g_hImagehlpDll, "SymGetLineFromAddr64" );
pSGMB = (tSGMB) GetProcAddress( g_hImagehlpDll, "SymGetModuleBase64" );
pSGMI = (tSGMI) GetProcAddress( g_hImagehlpDll, "SymGetModuleInfo64" );
pSGO = (tSGO) GetProcAddress( g_hImagehlpDll, "SymGetOptions" );
pSGSFA = (tSGSFA) GetProcAddress( g_hImagehlpDll, "SymGetSymFromAddr64" );
pSI = (tSI) GetProcAddress( g_hImagehlpDll, "SymInitialize" );
pSSO = (tSSO) GetProcAddress( g_hImagehlpDll, "SymSetOptions" );
pSW = (tSW) GetProcAddress( g_hImagehlpDll, "StackWalk64" );
pUDSN = (tUDSN) GetProcAddress( g_hImagehlpDll, "UnDecorateSymbolName" );
pSLM = (tSLM) GetProcAddress( g_hImagehlpDll, "SymLoadModule64" );
if ( pSC == NULL || pSFTA == NULL || pSGMB == NULL || pSGMI == NULL ||
pSGO == NULL || pSGSFA == NULL || pSI == NULL || pSSO == NULL ||
pSW == NULL || pUDSN == NULL || pSLM == NULL )
{
printf( "GetProcAddress(): some required function not found.\n" );
FreeLibrary( g_hImagehlpDll );
g_bInitialized = FALSE;
return 1;
}
g_bInitialized = TRUE;
InitializeCriticalSection(&g_csFileOpenClose);
return 0;
}
// This function if NOT multi-threading capable
// It should only be called from the main-Function!
int InitAllocCheckWN(eAllocCheckOutput eOutput, LPCTSTR pszFileName, ULONG ulShowStackAtAlloc) {
if (g_bInitialized) {
return 2; // already initialized!
}
if (ulShowStackAtAlloc <= 3)
g_ulShowStackAtAlloc = ulShowStackAtAlloc;
else
g_ulShowStackAtAlloc = 0;
if (pszFileName != NULL)
g_pszAllocLogName = _tcsdup(pszFileName);
else
g_pszAllocLogName = NULL;
g_CallstackOutputType = eOutput;
#ifdef _DEBUG
AllocHashInit();
#ifdef WITH_IMALLOC_SPY
HRESULT hr;
// erzeuge mein malloc-Spy object
LPMALLOCSPY pMallocSpy = new CMallocSpy(); // wird sp?er 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((UINT)-1,lString);
return lRet;
}
int InitAllocCheck(eAllocCheckOutput eOutput, BOOL bSetUnhandledExeptionFilter, ULONG ulShowStackAtAlloc) // will create the filename by itself
{
TCHAR szModName[_MAX_PATH];
if (GetModuleFileName(NULL, szModName, sizeof(szModName)/sizeof(TCHAR)) != 0)
{
_tcscpy(s_szExceptionLogFileName, szModName);
if (eOutput == ACOutput_XML)
_tcscat(s_szExceptionLogFileName, _T(".exp.xml"));
else
_tcscat(s_szExceptionLogFileName, _T(".exp.log"));
if (eOutput == ACOutput_XML)
_tcscat(szModName, _T(".mem.xml-leaks"));
else
_tcscat(szModName, _T(".mem.log"));
}
else
{
if (eOutput == ACOutput_XML)
_tcscpy(szModName, _T("\\mem-leaks.xml-leaks")); // default
else
_tcscpy(szModName, _T("\\mem-leaks.log")); // default
}
if ((bSetUnhandledExeptionFilter != FALSE) && (s_bUnhandledExeptionFilterSet == FALSE) )
{
// set global exception handler (for handling all unhandled exceptions)
SetUnhandledExceptionFilter(CrashHandlerExceptionFilter);
s_bUnhandledExeptionFilterSet = TRUE;
}
return InitAllocCheckWN(eOutput, szModName, ulShowStackAtAlloc);
}
// This function if NOT multi-threading capable
// It should only be called from the main-Function!
// Returns the number of bytes that are not freed (leaks)
ULONG DeInitAllocCheck(void) {
ULONG ulRet = 0;
if (g_bInitialized) {
#ifdef _DEBUG
InterlockedIncrement(&g_lMallocCalled); // No deactivate MyAllocHook, because StackWalker will allocate some memory)
ulRet = AllocHashDeinit(); // output the not freed memory
// remove the hook and set the old one
_CrtSetAllocHook(pfnOldCrtAllocHook);
#ifdef WITH_IMALLOC_SPY
CoRevokeMallocSpy();
#endif
#endif
EnterCriticalSection(&g_csFileOpenClose); // wait until a running stack dump was created
g_bInitialized = FALSE;
// de-init symbol handler etc. (SymCleanup())
if (pSC != NULL)
pSC( GetCurrentProcess() );
FreeLibrary( g_hImagehlpDll );
LeaveCriticalSection(&g_csFileOpenClose);
if (g_pszAllocLogName != NULL) {
free(g_pszAllocLogName);
g_pszAllocLogName = NULL;
}
if (g_fFile != NULL) {
fclose(g_fFile);
g_fFile = NULL;
}
DeleteCriticalSection(&g_csFileOpenClose);
InterlockedDecrement(&g_lMallocCalled);
}
if (s_bUnhandledExeptionFilterSet != TRUE)
{
SetUnhandledExceptionFilter(NULL);
s_bUnhandledExeptionFilterSet = FALSE;
}
return ulRet;
} // DeInitAllocCheck
void OnlyInstallUnhandeldExceptionFilter(eAllocCheckOutput eOutput)
{
if (s_bUnhandledExeptionFilterSet == FALSE)
{
TCHAR szModName[_MAX_PATH];
if (GetModuleFileName(NULL, szModName, sizeof(szModName)/sizeof(TCHAR)) != 0)
{
_tcscpy(s_szExceptionLogFileName, szModName);
if (eOutput == ACOutput_XML)
_tcscat(s_szExceptionLogFileName, _T(".exp.xml"));
else
_tcscat(s_szExceptionLogFileName, _T(".exp.log"));
if (eOutput == ACOutput_XML)
_tcscat(szModName, _T(".mem.xml-leaks"));
else
_tcscat(szModName, _T(".mem.log"));
}
else
{
if (eOutput == ACOutput_XML)
_tcscpy(szModName, _T("\\mem-leaks.xml-leaks")); // default
else
_tcscpy(szModName, _T("\\mem-leaks.log")); // default
}
// set it again; WARNING: this will override the setting for a possible AllocCheck-Setting
g_CallstackOutputType = eOutput;
// set global exception handler (for handling all unhandled exceptions)
SetUnhandledExceptionFilter(CrashHandlerExceptionFilter);
s_bUnhandledExeptionFilterSet = TRUE;
}
}
static TCHAR *GetExpectionCodeText(DWORD dwExceptionCode) {
switch(dwExceptionCode) {
case EXCEPTION_ACCESS_VIOLATION: return _T("ACCESS VIOLATION");
case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: return _T("ARRAY BOUNDS EXCEEDED");
case EXCEPTION_BREAKPOINT: return _T("BREAKPOINT");
case EXCEPTION_DATATYPE_MISALIGNMENT: return _T("DATATYPE MISALIGNMENT");
case EXCEPTION_FLT_DENORMAL_OPERAND: return _T("FLT DENORMAL OPERAND");
case EXCEPTION_FLT_DIVIDE_BY_ZERO: return _T("FLT DIVIDE BY ZERO");
case EXCEPTION_FLT_INEXACT_RESULT: return _T("FLT INEXACT RESULT");
case EXCEPTION_FLT_INVALID_OPERATION: return _T("FLT INVALID OPERATION");
case EXCEPTION_FLT_OVERFLOW: return _T("FLT OVERFLOW");
case EXCEPTION_FLT_STACK_CHECK: return _T("FLT STACK CHECK");
case EXCEPTION_FLT_UNDERFLOW: return _T("FLT UNDERFLOW");
case EXCEPTION_ILLEGAL_INSTRUCTION: return _T("ILLEGAL INSTRUCTION");
case EXCEPTION_IN_PAGE_ERROR: return _T("IN PAGE ERROR");
case EXCEPTION_INT_DIVIDE_BY_ZERO: return _T("INT DIVIDE BY ZERO");
case EXCEPTION_INT_OVERFLOW: return _T("INT OVERFLOW");
case EXCEPTION_INVALID_DISPOSITION: return _T("INVALID DISPOSITION");
case EXCEPTION_NONCONTINUABLE_EXCEPTION: return _T("NONCONTINUABLE EXCEPTION");
case EXCEPTION_PRIV_INSTRUCTION: return _T("PRIV INSTRUCTION");
case EXCEPTION_SINGLE_STEP: return _T("SINGLE STEP");
case EXCEPTION_STACK_OVERFLOW: return _T("STACK OVERFLOW");
case DBG_CONTROL_C : return _T("DBG CONTROL C ");
default:
return _T("<unkown exception>");
}
} // GetExpectionCodeText
// Function is not multi-threading safe, because of static char!
static TCHAR *GetAdditionalExpectionCodeText(PEXCEPTION_RECORD pExceptionRecord) {
static TCHAR szTemp[100];
switch(pExceptionRecord->ExceptionCode) {
case EXCEPTION_ACCESS_VIOLATION:
if (pExceptionRecord->NumberParameters == 2) {
switch(pExceptionRecord->ExceptionInformation[0]) {
case 0: // read attempt
_stprintf(szTemp, _T(" read attempt to address 0x%8.8X "), pExceptionRecord->ExceptionInformation[1]);
return szTemp;
case 1: // write attempt
_stprintf(szTemp, _T(" write attempt to address 0x%8.8X "), pExceptionRecord->ExceptionInformation[1]);
return szTemp;
default:
return _T("");
}
} // if (pExceptionRecord->NumberParameters == 2)
return _T("");
default:
return _T("");
} // switch(pExceptionRecord->ExceptionCode)
} // GetAdditionalExpectionCodeText
std::string SimpleXMLEncode(PCSTR szText)
{
std::string szRet;
for (size_t i=0; i<strlen(szText); i++)
{
switch(szText[i])
{
case '&':
szRet.append("&amp;");
break;
case '<':
szRet.append("&lt;");
break;
case '>':
szRet.append("&gt;");
break;
case '"':
szRet.append("&quot;");
break;
case '\'':
szRet.append("&apos;");
break;
default:
szRet += szText[i];
}
}
return szRet;
}
// #################################################################################
// #################################################################################
// Here the Stackwalk-Part begins.
// Some of the code is from an example from a book
// But I couldn´t find the reference anymore... sorry...
// If someone knowns, please let me know...
// #################################################################################
// #################################################################################
// if you use C++ exception handling: install a translator function
// with set_se_translator(). In the context of that function (but *not*
// afterwards), you can either do your stack dump, or save the CONTEXT
// record as a local copy. Note that you must do the stack sump at the
// earliest opportunity, to avoid the interesting stackframes being gone
// by the time you do the dump.
// status:
// - EXCEPTION_CONTINUE_SEARCH: exception wird weitergereicht
// - EXCEPTION_CONTINUE_EXECUTION:
// - EXCEPTION_EXECUTE_HANDLER:
DWORD StackwalkFilter( EXCEPTION_POINTERS *ep, DWORD status, LPCTSTR pszLogFile)
{
HANDLE hThread;
FILE *fFile = stdout; // default to stdout
if (pszLogFile != NULL) { // a filename is provided
// Open the logfile
fFile = _tfopen(pszLogFile, _T("a"));
if (fFile != NULL) { // Is the file too big?
long size;
fseek(fFile, 0, SEEK_END);
size = ftell(fFile); // Get the size of the file
if (size >= LOG_FILE_MAX_SIZE) {
TCHAR *pszTemp = (TCHAR*) malloc(MAX_PATH);
// It is too big...
fclose(fFile);
_tcscpy(pszTemp, pszLogFile);
_tcscat(pszTemp, _T(".old"));
_tremove(pszTemp); // Remove an old file, if exists
_trename(pszLogFile, pszTemp); // rename the actual file
fFile = _tfopen(pszLogFile, _T("w")); // create a new file
free(pszTemp);
}
}
} // if (pszLogFile != NULL)
if (fFile == NULL) {
fFile = stdout;
}
// Write infos about the exception
if (g_CallstackOutputType == ACOutput_XML)
{
_ftprintf(fFile, _T("<EXCEPTION code=\"%8.8X\" addr=\"%8.8X\" "),
ep->ExceptionRecord->ExceptionCode,
ep->ExceptionRecord->ExceptionAddress);
WriteDateTime(fFile, TRUE);
_ftprintf(fFile, _T("code_desc=\"%s\" more_desc=\"%s\">\n"), GetExpectionCodeText(ep->ExceptionRecord->ExceptionCode),
GetAdditionalExpectionCodeText(ep->ExceptionRecord));
}
else
{
_ftprintf(fFile, _T("######## EXCEPTION: 0x%8.8X at address: 0x%8.8X"),
ep->ExceptionRecord->ExceptionCode,
ep->ExceptionRecord->ExceptionAddress);
_ftprintf(fFile, _T(": %s %s\n"), GetExpectionCodeText(ep->ExceptionRecord->ExceptionCode),
GetAdditionalExpectionCodeText(ep->ExceptionRecord));
}
DuplicateHandle( GetCurrentProcess(), GetCurrentThread(),
GetCurrentProcess(), &hThread, 0, false, DUPLICATE_SAME_ACCESS );
ShowStack( hThread, *(ep->ContextRecord), fFile);
CloseHandle( hThread );
if (g_CallstackOutputType == ACOutput_XML)
_ftprintf(fFile, _T("</EXCEPTION>\n"));
fclose(fFile);
return status;
} // StackwalkFilter
void ShowStack( HANDLE hThread, CONTEXT& c, LPCTSTR pszLogFile)
{
FILE *fFile = stdout; // default to stdout
if (pszLogFile != NULL) { // a filename is available
// Open the logfile
fFile = _tfopen(pszLogFile, _T("a"));
if (fFile != NULL) { // Is the file too big?
long size;
fseek(fFile, 0, SEEK_END);
size = ftell(fFile); // Get the size of the file
if (size >= LOG_FILE_MAX_SIZE) {
TCHAR *pszTemp = (TCHAR*) malloc(MAX_PATH);
// It is too big...
fclose(fFile);
_tcscpy(pszTemp, pszLogFile);
_tcscat(pszTemp, _T(".old"));
_tremove(pszTemp); // Remove an old file, if exists
_trename(pszLogFile, pszTemp); // rename the actual file
fFile = _tfopen(pszLogFile, _T("w")); // open new file
free(pszTemp);
}
}
} // if (pszLogFile != NULL)
if (fFile == NULL) {
fFile = stdout;
}
ShowStack( hThread, c, fFile);
fclose(fFile);
}
static void ShowStack( HANDLE hThread, CONTEXT& c, FILE *fLogFile) {
ShowStackRM(hThread, c, fLogFile, NULL, GetCurrentProcess());
}
static void ShowStackRM( HANDLE hThread, CONTEXT& c, FILE *fLogFile, PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryFunction, HANDLE hSWProcess) {
// normally, call ImageNtHeader() and use machine info from PE header
// but we assume that it is an I386 image...
DWORD imageType = IMAGE_FILE_MACHINE_I386;
HANDLE hProcess = GetCurrentProcess(); // hProcess normally comes from outside but we only do the stackdump in our own process
int frameNum; // counts walked frames
DWORD64 offsetFromSymbol; // tells us how far from the symbol we were
DWORD offsetFromLine; // tells us how far from the line we were
DWORD symOptions; // symbol handler settings
static IMAGEHLP_SYMBOL64 *pSym = NULL;
char undName[MAXNAMELEN]; // undecorated name
char undFullName[MAXNAMELEN]; // undecorated name with all shenanigans
IMAGEHLP_MODULE64 Module;
IMAGEHLP_LINE64 Line;
BOOL bXMLTagWrote;
std::string symSearchPath;
static 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?e 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;
memset( pSym, '\0', IMGSYMLEN + MAXNAMELEN );
pSym->SizeOfStruct = IMGSYMLEN;
pSym->MaxNameLength = MAXNAMELEN;
memset( &Line, '\0', sizeof Line );
Line.SizeOfStruct = sizeof Line;
memset( &Module, '\0', sizeof Module );
Module.SizeOfStruct = sizeof Module;
for ( frameNum = 0; ; ++ frameNum )
{
// get next stack frame (StackWalk64(), SymFunctionTableAccess64(), SymGetModuleBase64())
// if this returns ERROR_INVALID_ADDRESS (487) or ERROR_NOACCESS (998), you can
// assume that either you are done, or that the stack is so hosed that the next
// deeper frame could not be found.
// CONTEXT need not to be suplied if imageTyp is IMAGE_FILE_MACHINE_I386!
if ( ! pSW( imageType, hSWProcess, hThread, &s, NULL, ReadMemoryFunction, pSFTA, pSGMB, NULL ) )
break;
bXMLTagWrote = FALSE;
if (g_CallstackOutputType == ACOutput_Advanced)
_ftprintf(fLogFile, _T("\n%lu: %3d"), g_dwShowCount, frameNum);
if ( s.AddrPC.Offset == 0 )
{
// Special case: If we are here, we have no valid callstack entry!
switch(g_CallstackOutputType)
{
case ACOutput_Simple:
_ftprintf(fLogFile, _T("%lu: (-nosymbols- PC == 0)\n"), g_dwShowCount);
break;
case ACOutput_Advanced:
_ftprintf(fLogFile, _T(" (-nosymbols- PC == 0)\n"));
break;
case ACOutput_XML:
// TODO: ....
_ftprintf(fLogFile, _T("<STACKENTRY decl=\"(-nosymbols- PC == 0)\"/>\n"));
break;
}
}
else
{
// we seem to have a valid PC
undName[0] = 0;
undFullName[0] = 0;
offsetFromSymbol = 0;
// show procedure info (SymGetSymFromAddr())
if ( ! pSGSFA( hProcess, s.AddrPC.Offset, &offsetFromSymbol, pSym ) )
{
if (g_CallstackOutputType == ACOutput_Advanced)
{
if ( gle != 487 )
_ftprintf(fLogFile, _T(" SymGetSymFromAddr(): GetLastError = %lu\n"), gle );
else
_ftprintf(fLogFile, _T("\n"));
}
}
else
{
// UnDecorateSymbolName()
pUDSN( pSym->Name, undName, MAXNAMELEN, UNDNAME_NAME_ONLY );
pUDSN( pSym->Name, undFullName, MAXNAMELEN, UNDNAME_COMPLETE );
if (g_CallstackOutputType == ACOutput_Advanced)
{
if (strlen(undName) > 0)
fprintf(fLogFile, " %s %+ld bytes\n", undName, (long) offsetFromSymbol );
else
{
fprintf(fLogFile, " Sig: %s %+ld bytes\n", pSym->Name, (long) offsetFromSymbol );
strcpy(undName, pSym->Name);
}
fprintf(fLogFile, "%lu: Decl: %s\n", g_dwShowCount, undFullName );
}
}
//if (g_CallstackOutputType == ACOutput_XML)
// fprintf(fLogFile, "decl=\"%s\" decl_offset=\"%+ld\" ", SimpleXMLEncode(undName).c_str(), (long) offsetFromSymbol);
// show line number info, NT5.0-method (SymGetLineFromAddr())
offsetFromLine = 0;
if ( pSGLFA != NULL )
{ // yes, we have SymGetLineFromAddr()
if ( ! pSGLFA( hProcess, s.AddrPC.Offset, &offsetFromLine, &Line ) )
{
if ( (gle != 487) && (frameNum > 0) ) // ignore error for first frame
{
if (g_CallstackOutputType == ACOutput_XML)
{
_ftprintf(fLogFile, _T("<STACKENTRY "));
bXMLTagWrote = TRUE;
fprintf(fLogFile, "decl=\"%s\" decl_offset=\"%+ld\" ", SimpleXMLEncode(undName).c_str(), (long) offsetFromSymbol);
_ftprintf(fLogFile, _T("srcfile=\"SymGetLineFromAddr(): GetLastError = %lu\" "), gle);
}
else
_ftprintf(fLogFile, _T("%lu: SymGetLineFromAddr(): GetLastError = %lu\n"), g_dwShowCount, gle );
}
}
else
{
switch(g_CallstackOutputType)
{
case ACOutput_Advanced:
fprintf(fLogFile, "%lu: Line: %s(%lu) %+ld bytes\n", g_dwShowCount,
Line.FileName, Line.LineNumber, offsetFromLine );
break;
case ACOutput_Simple:
fprintf(fLogFile, "%lu: %s(%lu) %+ld bytes (%s)\n", g_dwShowCount,
Line.FileName, Line.LineNumber, offsetFromLine, undName);
break;
case ACOutput_XML:
_ftprintf(fLogFile, _T("<STACKENTRY "));
bXMLTagWrote = TRUE;
fprintf(fLogFile, "decl=\"%s\" decl_offset=\"%+ld\" ", SimpleXMLEncode(undName).c_str(), (long) offsetFromSymbol);
fprintf(fLogFile, "srcfile=\"%s\" line=\"%lu\" line_offset=\"%+ld\" ",
SimpleXMLEncode(Line.FileName).c_str(), Line.LineNumber, offsetFromLine, undName);
break;
}
}
} // yes, we have SymGetLineFromAddr()
// show module info (SymGetModuleInfo())
if ( (g_CallstackOutputType == ACOutput_Advanced) || (g_CallstackOutputType == ACOutput_XML) )
{
if ( ! pSGMI( hProcess, s.AddrPC.Offset, &Module ) )
{
if (g_CallstackOutputType == ACOutput_Advanced)
_ftprintf(fLogFile, _T("%lu: SymGetModuleInfo): GetLastError = %lu\n"), g_dwShowCount, gle );
}
else
{ // got module info OK
char ty[80];
switch ( Module.SymType )
{
case SymNone:
strcpy( ty, "-nosymbols-" );
break;
case SymCoff:
strcpy( ty, "COFF" );
break;
case SymCv:
strcpy( ty, "CV" );
break;
case SymPdb:
strcpy( ty, "PDB" );
break;
case SymExport:
strcpy( ty, "-exported-" );
break;
case SymDeferred:
strcpy( ty, "-deferred-" );
break;
case SymSym:
strcpy( ty, "SYM" );
break;
#if API_VERSION_NUMBER >= 9
case SymDia:
strcpy( ty, "DIA" );
break;
#endif
default:
_snprintf( ty, sizeof ty, "symtype=%ld", (long) Module.SymType );
break;
}
if (g_CallstackOutputType == ACOutput_XML)
{
// now, check if the XML-Entry is written...
if (bXMLTagWrote == FALSE)
{
_ftprintf(fLogFile, _T("<STACKENTRY "));
bXMLTagWrote = TRUE;
fprintf(fLogFile, "decl=\"%s\" decl_offset=\"%+ld\" ", SimpleXMLEncode(undName).c_str(), (long) offsetFromSymbol);
_ftprintf(fLogFile, _T("srcfile=\"\" "));
bXMLTagWrote = TRUE;
}
}
if (g_CallstackOutputType == ACOutput_Advanced)
{
fprintf(fLogFile, "%lu: Mod: %s, base: %08lxh\n", g_dwShowCount,
Module.ModuleName, Module.BaseOfImage );
if (Module.SymType == SymNone) { // Gebe nur aus, wenn keine Symbole vorhanden sind!
_ftprintf(fLogFile, _T("%lu: Offset: 0x%8.8x\n"), g_dwShowCount, s.AddrPC.Offset);
fprintf(fLogFile, "%lu: Sym: type: %s, file: %s\n", g_dwShowCount,
ty, Module.LoadedImageName );
}
}
else
{
// XML:
if (bXMLTagWrote == TRUE)
fprintf(fLogFile, "module=\"%s\" base=\"%08lx\" ", Module.ModuleName, Module.BaseOfImage);
}
} // got module info OK
}
if ( (g_CallstackOutputType == ACOutput_XML) && (bXMLTagWrote == TRUE) )
_ftprintf(fLogFile, _T("/>\n")); // terminate the XML node
} // we seem to have a valid PC
// no return address means no deeper stackframe
if ( s.AddrReturn.Offset == 0 )
{
// avoid misunderstandings in the printf() following the loop
SetLastError( 0 );
break;
}
} // for ( frameNum )
if ( (g_CallstackOutputType != ACOutput_XML) && (gle != 0) )
_ftprintf(fLogFile, _T("\n%lu: StackWalk(): GetLastError = %lu\n"), g_dwShowCount, gle );
cleanup:
//if (pSym) free( pSym );
if (fLogFile) {
_ftprintf(fLogFile, _T("\n\n"));
if (g_dwShowCount % 1000)
fflush(fLogFile);
}
LeaveCriticalSection(&g_csFileOpenClose);
// Critical section end...
} // ShowStackRM
#pragma warning (pop)
#endif //
#endif //SUN_CODE_BACKUP