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
 |