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 typedef BOOL(__stdcall* PReadProcessMemoryRoutine)( 106 HANDLE hProcess, 107 DWORD64 qwBaseAddress, 108 PVOID lpBuffer, 109 DWORD nSize, 110 LPDWORD lpNumberOfBytesRead, 111 LPVOID pUserData // optional data, which was passed in "ShowCallstack" 112 ); 113 114 BOOL LoadModules(); 115 116 BOOL ShowCallstack( 117 HANDLE hThread = GetCurrentThread(), 118 const CONTEXT* context = NULL, 119 PReadProcessMemoryRoutine readMemoryFunction = NULL, 120 LPVOID pUserData = NULL // optional to identify some data in the 'readMemoryFunction'-callback 121 ); 122 123 BOOL ShowObject(LPVOID pObject); 124 125 #if _MSC_VER >= 1300 126 // due to some reasons, the "STACKWALK_MAX_NAMELEN" must be declared as "public" 127 // in older compilers in order to use it... starting with VC7 we can declare it as "protected" 128 protected: 129 #endif 130 enum 131 { 132 STACKWALK_MAX_NAMELEN = 1024 133 }; // max name length for found symbols 134 135 protected: 136 // Entry for each Callstack-Entry 137 typedef struct CallstackEntry 138 { 139 DWORD64 offset; // if 0, we have no valid entry 140 CHAR name[STACKWALK_MAX_NAMELEN]; 141 CHAR undName[STACKWALK_MAX_NAMELEN]; 142 CHAR undFullName[STACKWALK_MAX_NAMELEN]; 143 DWORD64 offsetFromSmybol; 144 DWORD offsetFromLine; 145 DWORD lineNumber; 146 CHAR lineFileName[STACKWALK_MAX_NAMELEN]; 147 DWORD symType; 148 LPCSTR symTypeString; 149 CHAR moduleName[STACKWALK_MAX_NAMELEN]; 150 DWORD64 baseOfImage; 151 CHAR loadedImageName[STACKWALK_MAX_NAMELEN]; 152 } CallstackEntry; 153 154 typedef enum CallstackEntryType 155 { 156 firstEntry, 157 nextEntry, 158 lastEntry 159 } CallstackEntryType; 160 161 virtual void OnSymInit(LPCSTR szSearchPath, DWORD symOptions, LPCSTR szUserName); 162 virtual void OnLoadModule(LPCSTR img, 163 LPCSTR mod, 164 DWORD64 baseAddr, 165 DWORD size, 166 DWORD result, 167 LPCSTR symType, 168 LPCSTR pdbName, 169 ULONGLONG fileVersion); 170 virtual void OnCallstackEntry(CallstackEntryType eType, CallstackEntry& entry); 171 virtual void OnDbgHelpErr(LPCSTR szFuncName, DWORD gle, DWORD64 addr); 172 virtual void OnOutput(LPCSTR szText); 173 174 StackWalkerInternal* m_sw; 175 HANDLE m_hProcess; 176 DWORD m_dwProcessId; 177 BOOL m_modulesLoaded; 178 LPSTR m_szSymPath; 179 180 int m_options; 181 int m_MaxRecursionCount; 182 183 static BOOL __stdcall myReadProcMem(HANDLE hProcess, 184 DWORD64 qwBaseAddress, 185 PVOID lpBuffer, 186 DWORD nSize, 187 LPDWORD lpNumberOfBytesRead); 188 189 friend StackWalkerInternal; 190 }; // class StackWalker 191 192 // The "ugly" assembler-implementation is needed for systems before XP 193 // If you have a new PSDK and you only compile for XP and later, then you can use 194 // the "RtlCaptureContext" 195 // Currently there is no define which determines the PSDK-Version... 196 // So we just use the compiler-version (and assumes that the PSDK is 197 // the one which was installed by the VS-IDE) 198 199 // INFO: If you want, you can use the RtlCaptureContext if you only target XP and later... 200 // But I currently use it in x64/IA64 environments... 201 //#if defined(_M_IX86) && (_WIN32_WINNT <= 0x0500) && (_MSC_VER < 1400) 202 203 #if defined(_M_IX86) 204 #ifdef CURRENT_THREAD_VIA_EXCEPTION 205 // TODO: The following is not a "good" implementation, 206 // because the callstack is only valid in the "__except" block... 207 #define GET_CURRENT_CONTEXT_STACKWALKER_CODEPLEX(c, contextFlags) \ 208 do \ 209 { \ 210 memset(&c, 0, sizeof(CONTEXT)); \ 211 EXCEPTION_POINTERS* pExp = NULL; \ 212 __try \ 213 { \ 214 throw 0; \ 215 } \ 216 __except (((pExp = GetExceptionInformation()) ? EXCEPTION_EXECUTE_HANDLER \ 217 : EXCEPTION_EXECUTE_HANDLER)) \ 218 { \ 219 } \ 220 if (pExp != NULL) \ 221 memcpy(&c, pExp->ContextRecord, sizeof(CONTEXT)); \ 222 c.ContextFlags = contextFlags; \ 223 } while (0); 224 #else 225 // clang-format off 226 // The following should be enough for walking the callstack... 227 #define GET_CURRENT_CONTEXT_STACKWALKER_CODEPLEX(c, contextFlags) \ 228 do \ 229 { \ 230 memset(&c, 0, sizeof(CONTEXT)); \ 231 c.ContextFlags = contextFlags; \ 232 __asm call x \ 233 __asm x: pop eax \ 234 __asm mov c.Eip, eax \ 235 __asm mov c.Ebp, ebp \ 236 __asm mov c.Esp, esp \ 237 } while (0) 238 // clang-format on 239 #endif 240 241 #else 242 243 // The following is defined for x86 (XP and higher), x64 and IA64: 244 #define GET_CURRENT_CONTEXT_STACKWALKER_CODEPLEX(c, contextFlags) \ 245 do \ 246 { \ 247 memset(&c, 0, sizeof(CONTEXT)); \ 248 c.ContextFlags = contextFlags; \ 249 RtlCaptureContext(&c); \ 250 } while (0) 251 #endif 252 253 #endif //defined(_MSC_VER) 254 255 #endif // __STACKWALKER_H__ 256