StackWalker.h (9933B)
1 #ifndef __STACKWALKER_H__ 2 #define __STACKWALKER_H__ 3 4 #if defined(_MSC_VER) 5 6 /********************************************************************** 7 * 8 * StackWalker.h 9 * 10 * 11 * 12 * LICENSE (http://www.opensource.org/licenses/bsd-license.php) 13 * 14 * Copyright (c) 2005-2009, Jochen Kalmbach 15 * All rights reserved. 16 * 17 * Redistribution and use in source and binary forms, with or without modification, 18 * are permitted provided that the following conditions are met: 19 * 20 * Redistributions of source code must retain the above copyright notice, 21 * this list of conditions and the following disclaimer. 22 * Redistributions in binary form must reproduce the above copyright notice, 23 * this list of conditions and the following disclaimer in the documentation 24 * and/or other materials provided with the distribution. 25 * Neither the name of Jochen Kalmbach nor the names of its contributors may be 26 * used to endorse or promote products derived from this software without 27 * specific prior written permission. 28 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 29 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 30 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 31 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 32 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 33 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 34 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 35 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 36 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 37 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 38 * 39 * **********************************************************************/ 40 // #pragma once is supported starting with _MSC_VER 1000, 41 // so we need not to check the version (because we only support _MSC_VER >= 1100)! 42 #pragma once 43 44 #include <windows.h> 45 46 #if _MSC_VER >= 1900 47 #pragma warning(disable : 4091) 48 #endif 49 50 // special defines for VC5/6 (if no actual PSDK is installed): 51 #if _MSC_VER < 1300 52 typedef unsigned __int64 DWORD64, *PDWORD64; 53 #if defined(_WIN64) 54 typedef unsigned __int64 SIZE_T, *PSIZE_T; 55 #else 56 typedef unsigned long SIZE_T, *PSIZE_T; 57 #endif 58 #endif // _MSC_VER < 1300 59 60 class StackWalkerInternal; // forward 61 class StackWalker 62 { 63 public: 64 typedef enum StackWalkOptions 65 { 66 // No addition info will be retrieved 67 // (only the address is available) 68 RetrieveNone = 0, 69 70 // Try to get the symbol-name 71 RetrieveSymbol = 1, 72 73 // Try to get the line for this symbol 74 RetrieveLine = 2, 75 76 // Try to retrieve the module-infos 77 RetrieveModuleInfo = 4, 78 79 // Also retrieve the version for the DLL/EXE 80 RetrieveFileVersion = 8, 81 82 // Contains all the above 83 RetrieveVerbose = 0xF, 84 85 // Generate a "good" symbol-search-path 86 SymBuildPath = 0x10, 87 88 // Also use the public Microsoft-Symbol-Server 89 SymUseSymSrv = 0x20, 90 91 // Contains all the above "Sym"-options 92 SymAll = 0x30, 93 94 // Contains all options (default) 95 OptionsAll = 0x3F 96 } StackWalkOptions; 97 98 StackWalker(int options = OptionsAll, // 'int' is by design, to combine the enum-flags 99 LPCSTR szSymPath = NULL, 100 DWORD dwProcessId = GetCurrentProcessId(), 101 HANDLE hProcess = GetCurrentProcess()); 102 StackWalker(DWORD dwProcessId, HANDLE hProcess); 103 virtual ~StackWalker(); 104 105 static HMODULE LoadDbgHelpLibrary(); 106 107 typedef BOOL(__stdcall* PReadProcessMemoryRoutine)( 108 HANDLE hProcess, 109 DWORD64 qwBaseAddress, 110 PVOID lpBuffer, 111 DWORD nSize, 112 LPDWORD lpNumberOfBytesRead, 113 LPVOID pUserData // optional data, which was passed in "ShowCallstack" 114 ); 115 116 BOOL LoadModules(); 117 118 BOOL ShowCallstack( 119 HANDLE hThread = GetCurrentThread(), 120 const CONTEXT* context = NULL, 121 PReadProcessMemoryRoutine readMemoryFunction = NULL, 122 LPVOID pUserData = NULL // optional to identify some data in the 'readMemoryFunction'-callback 123 ); 124 125 BOOL ShowObject(LPVOID pObject); 126 127 #if _MSC_VER >= 1300 128 // due to some reasons, the "STACKWALK_MAX_NAMELEN" must be declared as "public" 129 // in older compilers in order to use it... starting with VC7 we can declare it as "protected" 130 protected: 131 #endif 132 enum 133 { 134 STACKWALK_MAX_NAMELEN = 1024 135 }; // max name length for found symbols 136 137 protected: 138 // Entry for each Callstack-Entry 139 typedef struct CallstackEntry 140 { 141 DWORD64 offset; // if 0, we have no valid entry 142 CHAR name[STACKWALK_MAX_NAMELEN]; 143 CHAR undName[STACKWALK_MAX_NAMELEN]; 144 CHAR undFullName[STACKWALK_MAX_NAMELEN]; 145 DWORD64 offsetFromSmybol; 146 DWORD offsetFromLine; 147 DWORD lineNumber; 148 CHAR lineFileName[STACKWALK_MAX_NAMELEN]; 149 DWORD symType; 150 LPCSTR symTypeString; 151 CHAR moduleName[STACKWALK_MAX_NAMELEN]; 152 DWORD64 baseOfImage; 153 CHAR loadedImageName[STACKWALK_MAX_NAMELEN]; 154 } CallstackEntry; 155 156 typedef enum CallstackEntryType 157 { 158 firstEntry, 159 nextEntry, 160 lastEntry 161 } CallstackEntryType; 162 163 virtual void OnSymInit(LPCSTR szSearchPath, DWORD symOptions, LPCSTR szUserName); 164 virtual void OnLoadModule(LPCSTR img, 165 LPCSTR mod, 166 DWORD64 baseAddr, 167 DWORD size, 168 DWORD result, 169 LPCSTR symType, 170 LPCSTR pdbName, 171 ULONGLONG fileVersion); 172 virtual void OnCallstackEntry(CallstackEntryType eType, CallstackEntry& entry); 173 virtual void OnDbgHelpErr(LPCSTR szFuncName, DWORD gle, DWORD64 addr); 174 virtual void OnOutput(LPCSTR szText); 175 176 StackWalkerInternal* m_sw; 177 HANDLE m_hProcess; 178 DWORD m_dwProcessId; 179 BOOL m_modulesLoaded; 180 LPSTR m_szSymPath; 181 182 int m_options; 183 int m_MaxRecursionCount; 184 185 static BOOL __stdcall myReadProcMem(HANDLE hProcess, 186 DWORD64 qwBaseAddress, 187 PVOID lpBuffer, 188 DWORD nSize, 189 LPDWORD lpNumberOfBytesRead); 190 191 friend StackWalkerInternal; 192 }; // class StackWalker 193 194 // The "ugly" assembler-implementation is needed for systems before XP 195 // If you have a new PSDK and you only compile for XP and later, then you can use 196 // the "RtlCaptureContext" 197 // Currently there is no define which determines the PSDK-Version... 198 // So we just use the compiler-version (and assumes that the PSDK is 199 // the one which was installed by the VS-IDE) 200 201 // INFO: If you want, you can use the RtlCaptureContext if you only target XP and later... 202 // But I currently use it in x64/IA64 environments... 203 //#if defined(_M_IX86) && (_WIN32_WINNT <= 0x0500) && (_MSC_VER < 1400) 204 205 #if defined(_M_IX86) 206 #ifdef CURRENT_THREAD_VIA_EXCEPTION 207 // TODO: The following is not a "good" implementation, 208 // because the callstack is only valid in the "__except" block... 209 #define GET_CURRENT_CONTEXT_STACKWALKER_CODEPLEX(c, contextFlags) \ 210 do \ 211 { \ 212 memset(&c, 0, sizeof(CONTEXT)); \ 213 EXCEPTION_POINTERS* pExp = NULL; \ 214 __try \ 215 { \ 216 throw 0; \ 217 } \ 218 __except (((pExp = GetExceptionInformation()) ? EXCEPTION_EXECUTE_HANDLER \ 219 : EXCEPTION_EXECUTE_HANDLER)) \ 220 { \ 221 } \ 222 if (pExp != NULL) \ 223 memcpy(&c, pExp->ContextRecord, sizeof(CONTEXT)); \ 224 c.ContextFlags = contextFlags; \ 225 } while (0); 226 #else 227 // clang-format off 228 // The following should be enough for walking the callstack... 229 #define GET_CURRENT_CONTEXT_STACKWALKER_CODEPLEX(c, contextFlags) \ 230 do \ 231 { \ 232 memset(&c, 0, sizeof(CONTEXT)); \ 233 c.ContextFlags = contextFlags; \ 234 __asm call x \ 235 __asm x: pop eax \ 236 __asm mov c.Eip, eax \ 237 __asm mov c.Ebp, ebp \ 238 __asm mov c.Esp, esp \ 239 } while (0) 240 // clang-format on 241 #endif 242 243 #else 244 245 // The following is defined for x86 (XP and higher), x64 and IA64: 246 #define GET_CURRENT_CONTEXT_STACKWALKER_CODEPLEX(c, contextFlags) \ 247 do \ 248 { \ 249 memset(&c, 0, sizeof(CONTEXT)); \ 250 c.ContextFlags = contextFlags; \ 251 RtlCaptureContext(&c); \ 252 } while (0); 253 #endif 254 255 #endif //defined(_MSC_VER) 256 257 #endif // __STACKWALKER_H__