📄 stackwalker.cpp
字号:
/*////////////////////////////////////////////////////////////////////////////
* 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"
#include <windows.h>
#include <string>
#include <vector>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <crtdbg.h>
#include <tchar.h>
#include "Stackwalker.h"
// 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)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -