My Project
|
00001 /********************************************************************** 00002 * 00003 * StackWalker.h 00004 * 00005 * 00006 * History: 00007 * 2005-07-27 v1 - First public release on http://www.codeproject.com/ 00008 * (for additional changes see History in 'StackWalker.cpp'! 00009 * 00010 **********************************************************************/ 00011 // #pragma once is supported starting with _MCS_VER 1000, 00012 // so we need not to check the version (because we only support _MSC_VER >= 1100)! 00013 #pragma once 00014 00015 #include "Define.h" 00016 00017 // special defines for VC5/6 (if no actual PSDK is installed): 00018 #if _MSC_VER < 1300 00019 typedef unsigned __int64 DWORD64, *PDWORD64; 00020 #if defined(_WIN64) 00021 typedef unsigned __int64 SIZE_T, *PSIZE_T; 00022 #else 00023 typedef unsigned long SIZE_T, *PSIZE_T; 00024 #endif 00025 #endif // _MSC_VER < 1300 00026 00027 class StackWalkerInternal; // forward 00028 class DLL_API_SALLY StackWalker 00029 { 00030 public: 00031 typedef enum StackWalkOptions 00032 { 00033 // No addition info will be retrived 00034 // (only the address is available) 00035 RetrieveNone = 0, 00036 00037 // Try to get the symbol-name 00038 RetrieveSymbol = 1, 00039 00040 // Try to get the line for this symbol 00041 RetrieveLine = 2, 00042 00043 // Try to retrieve the module-infos 00044 RetrieveModuleInfo = 4, 00045 00046 // Also retrieve the version for the DLL/EXE 00047 RetrieveFileVersion = 8, 00048 00049 // Contains all the abouve 00050 RetrieveVerbose = 0xF, 00051 00052 // Generate a "good" symbol-search-path 00053 SymBuildPath = 0x10, 00054 00055 // Also use the public Microsoft-Symbol-Server 00056 SymUseSymSrv = 0x20, 00057 00058 // Contains all the abouve "Sym"-options 00059 SymAll = 0x30, 00060 00061 // Contains all options (default) 00062 OptionsAll = 0x3F 00063 } StackWalkOptions; 00064 00065 StackWalker( 00066 int options = OptionsAll, // 'int' is by design, to combine the enum-flags 00067 LPCSTR szSymPath = NULL, 00068 DWORD dwProcessId = GetCurrentProcessId(), 00069 HANDLE hProcess = GetCurrentProcess() 00070 ); 00071 StackWalker(DWORD dwProcessId, HANDLE hProcess); 00072 virtual ~StackWalker(); 00073 00074 typedef BOOL (__stdcall *PReadProcessMemoryRoutine)( 00075 HANDLE hProcess, 00076 DWORD64 qwBaseAddress, 00077 PVOID lpBuffer, 00078 DWORD nSize, 00079 LPDWORD lpNumberOfBytesRead, 00080 LPVOID pUserData // optional data, which was passed in "ShowCallstack" 00081 ); 00082 00083 BOOL LoadModules(); 00084 00085 BOOL ShowCallstack( 00086 HANDLE hThread = GetCurrentThread(), 00087 const CONTEXT *context = NULL, 00088 PReadProcessMemoryRoutine readMemoryFunction = NULL, 00089 LPVOID pUserData = NULL // optional to identify some data in the 'readMemoryFunction'-callback 00090 ); 00091 00092 #if _MSC_VER >= 1300 00093 // due to some reasons, the "STACKWALK_MAX_NAMELEN" must be declared as "public" 00094 // in older compilers in order to use it... starting with VC7 we can declare it as "protected" 00095 protected: 00096 #endif 00097 enum { STACKWALK_MAX_NAMELEN = 1024 }; // max name length for found symbols 00098 00099 protected: 00100 // Entry for each Callstack-Entry 00101 typedef struct CallstackEntry 00102 { 00103 DWORD64 offset; // if 0, we have no valid entry 00104 CHAR name[STACKWALK_MAX_NAMELEN]; 00105 CHAR undName[STACKWALK_MAX_NAMELEN]; 00106 CHAR undFullName[STACKWALK_MAX_NAMELEN]; 00107 DWORD64 offsetFromSmybol; 00108 DWORD offsetFromLine; 00109 DWORD lineNumber; 00110 CHAR lineFileName[STACKWALK_MAX_NAMELEN]; 00111 DWORD symType; 00112 LPCSTR symTypeString; 00113 CHAR moduleName[STACKWALK_MAX_NAMELEN]; 00114 DWORD64 baseOfImage; 00115 CHAR loadedImageName[STACKWALK_MAX_NAMELEN]; 00116 } CallstackEntry; 00117 00118 typedef enum CallstackEntryType {firstEntry, nextEntry, lastEntry}; 00119 00120 virtual void OnSymInit(LPCSTR szSearchPath, DWORD symOptions, LPCSTR szUserName); 00121 virtual void OnLoadModule(LPCSTR img, LPCSTR mod, DWORD64 baseAddr, DWORD size, DWORD result, LPCSTR symType, LPCSTR pdbName, ULONGLONG fileVersion); 00122 virtual void OnCallstackEntry(CallstackEntryType eType, CallstackEntry &entry); 00123 virtual void OnDbgHelpErr(LPCSTR szFuncName, DWORD gle, DWORD64 addr); 00124 virtual void OnOutput(LPCSTR szText); 00125 00126 StackWalkerInternal *m_sw; 00127 HANDLE m_hProcess; 00128 DWORD m_dwProcessId; 00129 BOOL m_modulesLoaded; 00130 LPSTR m_szSymPath; 00131 00132 int m_options; 00133 00134 static BOOL __stdcall myReadProcMem(HANDLE hProcess, DWORD64 qwBaseAddress, PVOID lpBuffer, DWORD nSize, LPDWORD lpNumberOfBytesRead); 00135 00136 friend StackWalkerInternal; 00137 }; 00138 00139 00140 // The "ugly" assembler-implementation is needed for systems before XP 00141 // If you have a new PSDK and you only compile for XP and later, then you can use 00142 // the "RtlCaptureContext" 00143 // Currently there is no define which determines the PSDK-Version... 00144 // So we just use the compiler-version (and assumes that the PSDK is 00145 // the one which was installed by the VS-IDE) 00146 00147 // INFO: If you want, you can use the RtlCaptureContext if you only target XP and later... 00148 // But I currently use it in x64/IA64 environments... 00149 //#if defined(_M_IX86) && (_WIN32_WINNT <= 0x0500) && (_MSC_VER < 1400) 00150 00151 #if defined(_M_IX86) 00152 #ifdef CURRENT_THREAD_VIA_EXCEPTION 00153 // TODO: The following is not a "good" implementation, 00154 // because the callstack is only valid in the "__except" block... 00155 #define GET_CURRENT_CONTEXT(c, contextFlags) \ 00156 do { \ 00157 memset(&c, 0, sizeof(CONTEXT)); \ 00158 EXCEPTION_POINTERS *pExp = NULL; \ 00159 __try { \ 00160 throw 0; \ 00161 } __except( ( (pExp = GetExceptionInformation()) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_EXECUTE_HANDLER)) {} \ 00162 if (pExp != NULL) \ 00163 memcpy(&c, pExp->ContextRecord, sizeof(CONTEXT)); \ 00164 c.ContextFlags = contextFlags; \ 00165 } while(0); 00166 #else 00167 // The following should be enough for walking the callstack... 00168 #define GET_CURRENT_CONTEXT(c, contextFlags) \ 00169 do { \ 00170 memset(&c, 0, sizeof(CONTEXT)); \ 00171 c.ContextFlags = contextFlags; \ 00172 __asm call x \ 00173 __asm x: pop eax \ 00174 __asm mov c.Eip, eax \ 00175 __asm mov c.Ebp, ebp \ 00176 __asm mov c.Esp, esp \ 00177 } while(0); 00178 #endif 00179 00180 #else 00181 00182 // The following is defined for x86 (XP and higher), x64 and IA64: 00183 #define GET_CURRENT_CONTEXT(c, contextFlags) \ 00184 do { \ 00185 memset(&c, 0, sizeof(CONTEXT)); \ 00186 c.ContextFlags = contextFlags; \ 00187 RtlCaptureContext(&c); \ 00188 } while(0); 00189 #endif