1 /**********************************************************************
7 * LICENSE (http://www.opensource.org/licenses/bsd-license.php)
9 * Copyright (c) 2005-2009, Jochen Kalmbach
10 * All rights reserved.
12 * Redistribution and use in source and binary forms, with or without modification,
13 * are permitted provided that the following conditions are met:
15 * Redistributions of source code must retain the above copyright notice,
16 * this list of conditions and the following disclaimer.
17 * Redistributions in binary form must reproduce the above copyright notice,
18 * this list of conditions and the following disclaimer in the documentation
19 * and/or other materials provided with the distribution.
20 * Neither the name of Jochen Kalmbach nor the names of its contributors may be
21 * used to endorse or promote products derived from this software without
22 * specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
24 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
25 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
28 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
30 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
32 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 * **********************************************************************/
35 // #pragma once is supported starting with _MCS_VER 1000,
36 // so we need not to check the version (because we only support _MSC_VER >= 1100)!
41 // special defines for VC5/6 (if no actual PSDK is installed):
43 typedef unsigned __int64 DWORD64, *PDWORD64;
45 typedef unsigned __int64 SIZE_T, *PSIZE_T;
47 typedef unsigned long SIZE_T, *PSIZE_T;
49 #endif // _MSC_VER < 1300
51 class StackWalkerInternal; // forward
55 typedef enum StackWalkOptions
57 // No addition info will be retrived
58 // (only the address is available)
61 // Try to get the symbol-name
64 // Try to get the line for this symbol
67 // Try to retrieve the module-infos
68 RetrieveModuleInfo = 4,
70 // Also retrieve the version for the DLL/EXE
71 RetrieveFileVersion = 8,
73 // Contains all the abouve
74 RetrieveVerbose = 0xF,
76 // Generate a "good" symbol-search-path
79 // Also use the public Microsoft-Symbol-Server
82 // Contains all the abouve "Sym"-options
85 // Contains all options (default)
90 int options = OptionsAll, // 'int' is by design, to combine the enum-flags
91 LPCSTR szSymPath = NULL,
92 DWORD dwProcessId = GetCurrentProcessId(),
93 HANDLE hProcess = GetCurrentProcess()
95 StackWalker(DWORD dwProcessId, HANDLE hProcess);
96 virtual ~StackWalker();
98 typedef BOOL (__stdcall *PReadProcessMemoryRoutine)(
100 DWORD64 qwBaseAddress,
103 LPDWORD lpNumberOfBytesRead,
104 LPVOID pUserData // optional data, which was passed in "ShowCallstack"
110 HANDLE hThread = GetCurrentThread(),
111 const CONTEXT *context = NULL,
112 PReadProcessMemoryRoutine readMemoryFunction = NULL,
113 LPVOID pUserData = NULL // optional to identify some data in the 'readMemoryFunction'-callback
117 // due to some reasons, the "STACKWALK_MAX_NAMELEN" must be declared as "public"
118 // in older compilers in order to use it... starting with VC7 we can declare it as "protected"
121 enum { STACKWALK_MAX_NAMELEN = 4096 }; // max name length for found symbols
124 // Entry for each Callstack-Entry
125 typedef struct CallstackEntry
127 DWORD64 offset; // if 0, we have no valid entry
128 CHAR name[STACKWALK_MAX_NAMELEN];
129 CHAR undName[STACKWALK_MAX_NAMELEN];
130 CHAR undFullName[STACKWALK_MAX_NAMELEN];
131 DWORD64 offsetFromSmybol;
132 DWORD offsetFromLine;
134 CHAR lineFileName[STACKWALK_MAX_NAMELEN];
136 LPCSTR symTypeString;
137 CHAR moduleName[STACKWALK_MAX_NAMELEN];
139 CHAR loadedImageName[STACKWALK_MAX_NAMELEN];
142 enum CallstackEntryType {firstEntry, nextEntry, lastEntry};
144 virtual void OnSymInit(LPCSTR szSearchPath, DWORD symOptions, LPCSTR szUserName);
145 virtual void OnLoadModule(LPCSTR img, LPCSTR mod, DWORD64 baseAddr, DWORD size, DWORD result, LPCSTR symType, LPCSTR pdbName, ULONGLONG fileVersion);
146 virtual void OnCallstackEntry(CallstackEntryType eType, CallstackEntry &entry);
147 virtual void OnDbgHelpErr(LPCSTR szFuncName, DWORD gle, DWORD64 addr);
148 virtual void OnOutput(LPCSTR szText);
150 StackWalkerInternal *m_sw;
153 BOOL m_modulesLoaded;
157 int m_MaxRecursionCount;
159 static BOOL __stdcall myReadProcMem(HANDLE hProcess, DWORD64 qwBaseAddress, PVOID lpBuffer, DWORD nSize, LPDWORD lpNumberOfBytesRead);
161 friend StackWalkerInternal;
162 }; // class StackWalker
165 // The "ugly" assembler-implementation is needed for systems before XP
166 // If you have a new PSDK and you only compile for XP and later, then you can use
167 // the "RtlCaptureContext"
168 // Currently there is no define which determines the PSDK-Version...
169 // So we just use the compiler-version (and assumes that the PSDK is
170 // the one which was installed by the VS-IDE)
172 // INFO: If you want, you can use the RtlCaptureContext if you only target XP and later...
173 // But I currently use it in x64/IA64 environments...
174 //#if defined(_M_IX86) && (_WIN32_WINNT <= 0x0500) && (_MSC_VER < 1400)
177 #ifdef CURRENT_THREAD_VIA_EXCEPTION
178 // TODO: The following is not a "good" implementation,
179 // because the callstack is only valid in the "__except" block...
180 #define GET_CURRENT_CONTEXT_STACKWALKER_CODEPLEX(c, contextFlags) \
182 memset(&c, 0, sizeof(CONTEXT)); \
183 EXCEPTION_POINTERS *pExp = NULL; \
186 } __except( ( (pExp = GetExceptionInformation()) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_EXECUTE_HANDLER)) {} \
188 memcpy(&c, pExp->ContextRecord, sizeof(CONTEXT)); \
189 c.ContextFlags = contextFlags; \
192 // The following should be enough for walking the callstack...
193 #define GET_CURRENT_CONTEXT_STACKWALKER_CODEPLEX(c, contextFlags) \
195 memset(&c, 0, sizeof(CONTEXT)); \
196 c.ContextFlags = contextFlags; \
199 __asm mov c.Eip, eax \
200 __asm mov c.Ebp, ebp \
201 __asm mov c.Esp, esp \
207 // The following is defined for x86 (XP and higher), x64 and IA64:
208 #define GET_CURRENT_CONTEXT_STACKWALKER_CODEPLEX(c, contextFlags) \
210 memset(&c, 0, sizeof(CONTEXT)); \
211 c.ContextFlags = contextFlags; \
212 RtlCaptureContext(&c); \