190 lines
		
	
	
		
			5.8 KiB
		
	
	
	
		
			C
		
	
	
	
		
		
			
		
	
	
			190 lines
		
	
	
		
			5.8 KiB
		
	
	
	
		
			C
		
	
	
	
| 
								 | 
							
								/**********************************************************************
							 | 
						||
| 
								 | 
							
								 * 
							 | 
						||
| 
								 | 
							
								 * StackWalker.h
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * History:
							 | 
						||
| 
								 | 
							
								 *  2005-07-27   v1    - First public release on http://www.codeproject.com/
							 | 
						||
| 
								 | 
							
								 *  (for additional changes see History in 'StackWalker.cpp'!
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 **********************************************************************/
							 | 
						||
| 
								 | 
							
								// #pragma once is supported starting with _MCS_VER 1000, 
							 | 
						||
| 
								 | 
							
								// so we need not to check the version (because we only support _MSC_VER >= 1100)!
							 | 
						||
| 
								 | 
							
								#pragma once
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#include <windows.h>
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// special defines for VC5/6 (if no actual PSDK is installed):
							 | 
						||
| 
								 | 
							
								#if _MSC_VER < 1300
							 | 
						||
| 
								 | 
							
								typedef unsigned __int64 DWORD64, *PDWORD64;
							 | 
						||
| 
								 | 
							
								#if defined(_WIN64)
							 | 
						||
| 
								 | 
							
								typedef unsigned __int64 SIZE_T, *PSIZE_T;
							 | 
						||
| 
								 | 
							
								#else
							 | 
						||
| 
								 | 
							
								typedef unsigned long SIZE_T, *PSIZE_T;
							 | 
						||
| 
								 | 
							
								#endif
							 | 
						||
| 
								 | 
							
								#endif  // _MSC_VER < 1300
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class StackWalkerInternal;  // forward
							 | 
						||
| 
								 | 
							
								class StackWalker
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								public:
							 | 
						||
| 
								 | 
							
								  typedef enum StackWalkOptions
							 | 
						||
| 
								 | 
							
								  {
							 | 
						||
| 
								 | 
							
								    // No addition info will be retrived 
							 | 
						||
| 
								 | 
							
								    // (only the address is available)
							 | 
						||
| 
								 | 
							
								    RetrieveNone = 0,
							 | 
						||
| 
								 | 
							
								    
							 | 
						||
| 
								 | 
							
								    // Try to get the symbol-name
							 | 
						||
| 
								 | 
							
								    RetrieveSymbol = 1,
							 | 
						||
| 
								 | 
							
								    
							 | 
						||
| 
								 | 
							
								    // Try to get the line for this symbol
							 | 
						||
| 
								 | 
							
								    RetrieveLine = 2,
							 | 
						||
| 
								 | 
							
								    
							 | 
						||
| 
								 | 
							
								    // Try to retrieve the module-infos
							 | 
						||
| 
								 | 
							
								    RetrieveModuleInfo = 4,
							 | 
						||
| 
								 | 
							
								    
							 | 
						||
| 
								 | 
							
								    // Also retrieve the version for the DLL/EXE
							 | 
						||
| 
								 | 
							
								    RetrieveFileVersion = 8,
							 | 
						||
| 
								 | 
							
								    
							 | 
						||
| 
								 | 
							
								    // Contains all the abouve
							 | 
						||
| 
								 | 
							
								    RetrieveVerbose = 0xF,
							 | 
						||
| 
								 | 
							
								    
							 | 
						||
| 
								 | 
							
								    // Generate a "good" symbol-search-path
							 | 
						||
| 
								 | 
							
								    SymBuildPath = 0x10,
							 | 
						||
| 
								 | 
							
								    
							 | 
						||
| 
								 | 
							
								    // Also use the public Microsoft-Symbol-Server
							 | 
						||
| 
								 | 
							
								    SymUseSymSrv = 0x20,
							 | 
						||
| 
								 | 
							
								    
							 | 
						||
| 
								 | 
							
								    // Contains all the abouve "Sym"-options
							 | 
						||
| 
								 | 
							
								    SymAll = 0x30,
							 | 
						||
| 
								 | 
							
								    
							 | 
						||
| 
								 | 
							
								    // Contains all options (default)
							 | 
						||
| 
								 | 
							
								    OptionsAll = 0x3F
							 | 
						||
| 
								 | 
							
								  } StackWalkOptions;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  StackWalker(
							 | 
						||
| 
								 | 
							
								    int options = OptionsAll, // 'int' is by design, to combine the enum-flags
							 | 
						||
| 
								 | 
							
								    LPCSTR szSymPath = NULL, 
							 | 
						||
| 
								 | 
							
								    DWORD dwProcessId = GetCurrentProcessId(), 
							 | 
						||
| 
								 | 
							
								    HANDLE hProcess = GetCurrentProcess()
							 | 
						||
| 
								 | 
							
								    );
							 | 
						||
| 
								 | 
							
								  StackWalker(DWORD dwProcessId, HANDLE hProcess);
							 | 
						||
| 
								 | 
							
								  virtual ~StackWalker();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  typedef BOOL (__stdcall *PReadProcessMemoryRoutine)(
							 | 
						||
| 
								 | 
							
								    HANDLE      hProcess,
							 | 
						||
| 
								 | 
							
								    DWORD64     qwBaseAddress,
							 | 
						||
| 
								 | 
							
								    PVOID       lpBuffer,
							 | 
						||
| 
								 | 
							
								    DWORD       nSize,
							 | 
						||
| 
								 | 
							
								    LPDWORD     lpNumberOfBytesRead,
							 | 
						||
| 
								 | 
							
								    LPVOID      pUserData  // optional data, which was passed in "ShowCallstack"
							 | 
						||
| 
								 | 
							
								    );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  BOOL LoadModules();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  BOOL ShowCallstack(
							 | 
						||
| 
								 | 
							
								    HANDLE hThread = GetCurrentThread(), 
							 | 
						||
| 
								 | 
							
								    const CONTEXT *context = NULL, 
							 | 
						||
| 
								 | 
							
								    PReadProcessMemoryRoutine readMemoryFunction = NULL,
							 | 
						||
| 
								 | 
							
								    LPVOID pUserData = NULL  // optional to identify some data in the 'readMemoryFunction'-callback
							 | 
						||
| 
								 | 
							
								    );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#if _MSC_VER >= 1300
							 | 
						||
| 
								 | 
							
								// due to some reasons, the "STACKWALK_MAX_NAMELEN" must be declared as "public" 
							 | 
						||
| 
								 | 
							
								// in older compilers in order to use it... starting with VC7 we can declare it as "protected"
							 | 
						||
| 
								 | 
							
								protected:
							 | 
						||
| 
								 | 
							
								#endif
							 | 
						||
| 
								 | 
							
									enum { STACKWALK_MAX_NAMELEN = 1024 }; // max name length for found symbols
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								protected:
							 | 
						||
| 
								 | 
							
								  // Entry for each Callstack-Entry
							 | 
						||
| 
								 | 
							
								  typedef struct CallstackEntry
							 | 
						||
| 
								 | 
							
								  {
							 | 
						||
| 
								 | 
							
								    DWORD64 offset;  // if 0, we have no valid entry
							 | 
						||
| 
								 | 
							
								    CHAR name[STACKWALK_MAX_NAMELEN];
							 | 
						||
| 
								 | 
							
								    CHAR undName[STACKWALK_MAX_NAMELEN];
							 | 
						||
| 
								 | 
							
								    CHAR undFullName[STACKWALK_MAX_NAMELEN];
							 | 
						||
| 
								 | 
							
								    DWORD64 offsetFromSmybol;
							 | 
						||
| 
								 | 
							
								    DWORD offsetFromLine;
							 | 
						||
| 
								 | 
							
								    DWORD lineNumber;
							 | 
						||
| 
								 | 
							
								    CHAR lineFileName[STACKWALK_MAX_NAMELEN];
							 | 
						||
| 
								 | 
							
								    DWORD symType;
							 | 
						||
| 
								 | 
							
								    LPCSTR symTypeString;
							 | 
						||
| 
								 | 
							
								    CHAR moduleName[STACKWALK_MAX_NAMELEN];
							 | 
						||
| 
								 | 
							
								    DWORD64 baseOfImage;
							 | 
						||
| 
								 | 
							
								    CHAR loadedImageName[STACKWALK_MAX_NAMELEN];
							 | 
						||
| 
								 | 
							
								  } CallstackEntry;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  enum CallstackEntryType {firstEntry, nextEntry, lastEntry};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  virtual void OnSymInit(LPCSTR szSearchPath, DWORD symOptions, LPCSTR szUserName);
							 | 
						||
| 
								 | 
							
								  virtual void OnLoadModule(LPCSTR img, LPCSTR mod, DWORD64 baseAddr, DWORD size, DWORD result, LPCSTR symType, LPCSTR pdbName, ULONGLONG fileVersion);
							 | 
						||
| 
								 | 
							
								  virtual void OnCallstackEntry(CallstackEntryType eType, CallstackEntry &entry);
							 | 
						||
| 
								 | 
							
								  virtual void OnDbgHelpErr(LPCSTR szFuncName, DWORD gle, DWORD64 addr);
							 | 
						||
| 
								 | 
							
								  virtual void OnOutput(LPCSTR szText);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  StackWalkerInternal *m_sw;
							 | 
						||
| 
								 | 
							
								  HANDLE m_hProcess;
							 | 
						||
| 
								 | 
							
								  DWORD m_dwProcessId;
							 | 
						||
| 
								 | 
							
								  BOOL m_modulesLoaded;
							 | 
						||
| 
								 | 
							
								  LPSTR m_szSymPath;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  int m_options;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  static BOOL __stdcall myReadProcMem(HANDLE hProcess, DWORD64 qwBaseAddress, PVOID lpBuffer, DWORD nSize, LPDWORD lpNumberOfBytesRead);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  friend StackWalkerInternal;
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// The "ugly" assembler-implementation is needed for systems before XP
							 | 
						||
| 
								 | 
							
								// If you have a new PSDK and you only compile for XP and later, then you can use 
							 | 
						||
| 
								 | 
							
								// the "RtlCaptureContext"
							 | 
						||
| 
								 | 
							
								// Currently there is no define which determines the PSDK-Version... 
							 | 
						||
| 
								 | 
							
								// So we just use the compiler-version (and assumes that the PSDK is 
							 | 
						||
| 
								 | 
							
								// the one which was installed by the VS-IDE)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// INFO: If you want, you can use the RtlCaptureContext if you only target XP and later...
							 | 
						||
| 
								 | 
							
								//       But I currently use it in x64/IA64 environments...
							 | 
						||
| 
								 | 
							
								//#if defined(_M_IX86) && (_WIN32_WINNT <= 0x0500) && (_MSC_VER < 1400)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#if defined(_M_IX86)
							 | 
						||
| 
								 | 
							
								#ifdef CURRENT_THREAD_VIA_EXCEPTION
							 | 
						||
| 
								 | 
							
								// TODO: The following is not a "good" implementation, 
							 | 
						||
| 
								 | 
							
								// because the callstack is only valid in the "__except" block...
							 | 
						||
| 
								 | 
							
								#define GET_CURRENT_CONTEXT(c, contextFlags) \
							 | 
						||
| 
								 | 
							
								  do { \
							 | 
						||
| 
								 | 
							
								    memset(&c, 0, sizeof(CONTEXT)); \
							 | 
						||
| 
								 | 
							
								    EXCEPTION_POINTERS *pExp = NULL; \
							 | 
						||
| 
								 | 
							
								    __try { \
							 | 
						||
| 
								 | 
							
								      throw 0; \
							 | 
						||
| 
								 | 
							
								    } __except( ( (pExp = GetExceptionInformation()) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_EXECUTE_HANDLER)) {} \
							 | 
						||
| 
								 | 
							
								    if (pExp != NULL) \
							 | 
						||
| 
								 | 
							
								      memcpy(&c, pExp->ContextRecord, sizeof(CONTEXT)); \
							 | 
						||
| 
								 | 
							
								      c.ContextFlags = contextFlags; \
							 | 
						||
| 
								 | 
							
								  } while(0);
							 | 
						||
| 
								 | 
							
								#else
							 | 
						||
| 
								 | 
							
								// The following should be enough for walking the callstack...
							 | 
						||
| 
								 | 
							
								#define GET_CURRENT_CONTEXT(c, contextFlags) \
							 | 
						||
| 
								 | 
							
								  do { \
							 | 
						||
| 
								 | 
							
								    memset(&c, 0, sizeof(CONTEXT)); \
							 | 
						||
| 
								 | 
							
								    c.ContextFlags = contextFlags; \
							 | 
						||
| 
								 | 
							
								    __asm    call x \
							 | 
						||
| 
								 | 
							
								    __asm x: pop eax \
							 | 
						||
| 
								 | 
							
								    __asm    mov c.Eip, eax \
							 | 
						||
| 
								 | 
							
								    __asm    mov c.Ebp, ebp \
							 | 
						||
| 
								 | 
							
								    __asm    mov c.Esp, esp \
							 | 
						||
| 
								 | 
							
								  } while(0);
							 | 
						||
| 
								 | 
							
								#endif
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#else
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// The following is defined for x86 (XP and higher), x64 and IA64:
							 | 
						||
| 
								 | 
							
								#define GET_CURRENT_CONTEXT(c, contextFlags) \
							 | 
						||
| 
								 | 
							
								  do { \
							 | 
						||
| 
								 | 
							
								    memset(&c, 0, sizeof(CONTEXT)); \
							 | 
						||
| 
								 | 
							
								    c.ContextFlags = contextFlags; \
							 | 
						||
| 
								 | 
							
								    RtlCaptureContext(&c); \
							 | 
						||
| 
								 | 
							
								} while(0);
							 | 
						||
| 
								 | 
							
								#endif
							 |