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
 |