00001 /********************************************************************** 00002 * 00003 * StackWalker.h 00004 * 00005 * 00006 * 00007 * LICENSE (http://www.opensource.org/licenses/bsd-license.php) 00008 * 00009 * Copyright (c) 2005-2009, Jochen Kalmbach 00010 * All rights reserved. 00011 * 00012 * Redistribution and use in source and binary forms, with or without modification, 00013 * are permitted provided that the following conditions are met: 00014 * 00015 * Redistributions of source code must retain the above copyright notice, 00016 * this list of conditions and the following disclaimer. 00017 * Redistributions in binary form must reproduce the above copyright notice, 00018 * this list of conditions and the following disclaimer in the documentation 00019 * and/or other materials provided with the distribution. 00020 * Neither the name of Jochen Kalmbach nor the names of its contributors may be 00021 * used to endorse or promote products derived from this software without 00022 * specific prior written permission. 00023 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 00024 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 00025 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 00026 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 00027 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 00028 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 00029 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 00030 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 00031 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 00032 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 00033 * 00034 * **********************************************************************/ 00035 // #pragma once is supported starting with _MCS_VER 1000, 00036 // so we need not to check the version (because we only support _MSC_VER >= 1100)! 00037 #pragma once 00038 00039 #include <windows.h> 00040 00041 // special defines for VC5/6 (if no actual PSDK is installed): 00042 #if _MSC_VER < 1300 00043 typedef unsigned __int64 DWORD64, *PDWORD64; 00044 #if defined(_WIN64) 00045 typedef unsigned __int64 SIZE_T, *PSIZE_T; 00046 #else 00047 typedef unsigned long SIZE_T, *PSIZE_T; 00048 #endif 00049 #endif // _MSC_VER < 1300 00050 00051 class StackWalkerInternal; // forward 00052 class StackWalker 00053 { 00054 public: 00055 typedef enum StackWalkOptions 00056 { 00057 // No addition info will be retrived 00058 // (only the address is available) 00059 RetrieveNone = 0, 00060 00061 // Try to get the symbol-name 00062 RetrieveSymbol = 1, 00063 00064 // Try to get the line for this symbol 00065 RetrieveLine = 2, 00066 00067 // Try to retrieve the module-infos 00068 RetrieveModuleInfo = 4, 00069 00070 // Also retrieve the version for the DLL/EXE 00071 RetrieveFileVersion = 8, 00072 00073 // Contains all the abouve 00074 RetrieveVerbose = 0xF, 00075 00076 // Generate a "good" symbol-search-path 00077 SymBuildPath = 0x10, 00078 00079 // Also use the public Microsoft-Symbol-Server 00080 SymUseSymSrv = 0x20, 00081 00082 // Contains all the abouve "Sym"-options 00083 SymAll = 0x30, 00084 00085 // Contains all options (default) 00086 OptionsAll = 0x3F 00087 } StackWalkOptions; 00088 00089 StackWalker( 00090 int options = OptionsAll, // 'int' is by design, to combine the enum-flags 00091 LPCSTR szSymPath = NULL, 00092 DWORD dwProcessId = GetCurrentProcessId(), 00093 HANDLE hProcess = GetCurrentProcess() 00094 ); 00095 StackWalker(DWORD dwProcessId, HANDLE hProcess); 00096 virtual ~StackWalker(); 00097 00098 typedef BOOL (__stdcall *PReadProcessMemoryRoutine)( 00099 HANDLE hProcess, 00100 DWORD64 qwBaseAddress, 00101 PVOID lpBuffer, 00102 DWORD nSize, 00103 LPDWORD lpNumberOfBytesRead, 00104 LPVOID pUserData // optional data, which was passed in "ShowCallstack" 00105 ); 00106 00107 BOOL LoadModules(); 00108 00109 BOOL ShowCallstack( 00110 HANDLE hThread = GetCurrentThread(), 00111 const CONTEXT *context = NULL, 00112 PReadProcessMemoryRoutine readMemoryFunction = NULL, 00113 LPVOID pUserData = NULL // optional to identify some data in the 'readMemoryFunction'-callback 00114 ); 00115 00116 #if _MSC_VER >= 1300 00117 // due to some reasons, the "STACKWALK_MAX_NAMELEN" must be declared as "public" 00118 // in older compilers in order to use it... starting with VC7 we can declare it as "protected" 00119 protected: 00120 #endif 00121 enum { STACKWALK_MAX_NAMELEN = 1024 }; // max name length for found symbols 00122 00123 protected: 00124 // Entry for each Callstack-Entry 00125 typedef struct CallstackEntry 00126 { 00127 DWORD64 offset; // if 0, we have no valid entry 00128 CHAR name[STACKWALK_MAX_NAMELEN]; 00129 CHAR undName[STACKWALK_MAX_NAMELEN]; 00130 CHAR undFullName[STACKWALK_MAX_NAMELEN]; 00131 DWORD64 offsetFromSmybol; 00132 DWORD offsetFromLine; 00133 DWORD lineNumber; 00134 CHAR lineFileName[STACKWALK_MAX_NAMELEN]; 00135 DWORD symType; 00136 LPCSTR symTypeString; 00137 CHAR moduleName[STACKWALK_MAX_NAMELEN]; 00138 DWORD64 baseOfImage; 00139 CHAR loadedImageName[STACKWALK_MAX_NAMELEN]; 00140 } CallstackEntry; 00141 00142 typedef enum CallstackEntryType {firstEntry, nextEntry, lastEntry}; 00143 00144 virtual void OnSymInit(LPCSTR szSearchPath, DWORD symOptions, LPCSTR szUserName); 00145 virtual void OnLoadModule(LPCSTR img, LPCSTR mod, DWORD64 baseAddr, DWORD size, DWORD result, LPCSTR symType, LPCSTR pdbName, ULONGLONG fileVersion); 00146 virtual void OnCallstackEntry(CallstackEntryType eType, CallstackEntry &entry); 00147 virtual void OnDbgHelpErr(LPCSTR szFuncName, DWORD gle, DWORD64 addr); 00148 virtual void OnOutput(LPCSTR szText); 00149 00150 StackWalkerInternal *m_sw; 00151 HANDLE m_hProcess; 00152 DWORD m_dwProcessId; 00153 BOOL m_modulesLoaded; 00154 LPSTR m_szSymPath; 00155 00156 int m_options; 00157 int m_MaxRecursionCount; 00158 00159 static BOOL __stdcall myReadProcMem(HANDLE hProcess, DWORD64 qwBaseAddress, PVOID lpBuffer, DWORD nSize, LPDWORD lpNumberOfBytesRead); 00160 00161 friend StackWalkerInternal; 00162 }; // class StackWalker 00163 00164 00165 // The "ugly" assembler-implementation is needed for systems before XP 00166 // If you have a new PSDK and you only compile for XP and later, then you can use 00167 // the "RtlCaptureContext" 00168 // Currently there is no define which determines the PSDK-Version... 00169 // So we just use the compiler-version (and assumes that the PSDK is 00170 // the one which was installed by the VS-IDE) 00171 00172 // INFO: If you want, you can use the RtlCaptureContext if you only target XP and later... 00173 // But I currently use it in x64/IA64 environments... 00174 //#if defined(_M_IX86) && (_WIN32_WINNT <= 0x0500) && (_MSC_VER < 1400) 00175 00176 #if defined(_M_IX86) 00177 #ifdef CURRENT_THREAD_VIA_EXCEPTION 00178 // TODO: The following is not a "good" implementation, 00179 // because the callstack is only valid in the "__except" block... 00180 #define GET_CURRENT_CONTEXT(c, contextFlags) \ 00181 do { \ 00182 memset(&c, 0, sizeof(CONTEXT)); \ 00183 EXCEPTION_POINTERS *pExp = NULL; \ 00184 __try { \ 00185 throw 0; \ 00186 } __except( ( (pExp = GetExceptionInformation()) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_EXECUTE_HANDLER)) {} \ 00187 if (pExp != NULL) \ 00188 memcpy(&c, pExp->ContextRecord, sizeof(CONTEXT)); \ 00189 c.ContextFlags = contextFlags; \ 00190 } while(0); 00191 #else 00192 // The following should be enough for walking the callstack... 00193 #define GET_CURRENT_CONTEXT(c, contextFlags) \ 00194 do { \ 00195 memset(&c, 0, sizeof(CONTEXT)); \ 00196 c.ContextFlags = contextFlags; \ 00197 __asm call x \ 00198 __asm x: pop eax \ 00199 __asm mov c.Eip, eax \ 00200 __asm mov c.Ebp, ebp \ 00201 __asm mov c.Esp, esp \ 00202 } while(0); 00203 #endif 00204 00205 #else 00206 00207 // The following is defined for x86 (XP and higher), x64 and IA64: 00208 #define GET_CURRENT_CONTEXT(c, contextFlags) \ 00209 do { \ 00210 memset(&c, 0, sizeof(CONTEXT)); \ 00211 c.ContextFlags = contextFlags; \ 00212 RtlCaptureContext(&c); \ 00213 } while(0); 00214 #endif