📄 stackwalker.cpp
字号:
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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -