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