Developer Documentation

StackWalker.hh
1 /**********************************************************************
2 *
3 * StackWalker.h
4 *
5 *
6 *
7 * LICENSE (http://www.opensource.org/licenses/bsd-license.php)
8 *
9 * Copyright (c) 2005-2009, Jochen Kalmbach
10 * All rights reserved.
11 *
12 * Redistribution and use in source and binary forms, with or without modification,
13 * are permitted provided that the following conditions are met:
14 *
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.
33 *
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)!
37 #pragma once
38
39 #include <windows.h>
40
41 #if _MSC_VER >= 1900
42 #pragma warning(disable : 4091)
43 #endif
44
45 // special defines for VC5/6 (if no actual PSDK is installed):
46 #if _MSC_VER < 1300
47 typedef unsigned __int64 DWORD64, *PDWORD64;
48 #if defined(_WIN64)
49 typedef unsigned __int64 SIZE_T, *PSIZE_T;
50 #else
51 typedef unsigned long SIZE_T, *PSIZE_T;
52 #endif
53 #endif // _MSC_VER < 1300
54
55 class StackWalkerInternal; // forward
56 class StackWalker
57 {
58 public:
59 typedef enum StackWalkOptions
60 {
61 // No addition info will be retrived
62 // (only the address is available)
63 RetrieveNone = 0,
64
65 // Try to get the symbol-name
66 RetrieveSymbol = 1,
67
68 // Try to get the line for this symbol
69 RetrieveLine = 2,
70
71 // Try to retrieve the module-infos
72 RetrieveModuleInfo = 4,
73
74 // Also retrieve the version for the DLL/EXE
75 RetrieveFileVersion = 8,
76
77 // Contains all the abouve
78 RetrieveVerbose = 0xF,
79
80 // Generate a "good" symbol-search-path
81 SymBuildPath = 0x10,
82
83 // Also use the public Microsoft-Symbol-Server
84 SymUseSymSrv = 0x20,
85
86 // Contains all the abouve "Sym"-options
87 SymAll = 0x30,
88
89 // Contains all options (default)
90 OptionsAll = 0x3F
91 } StackWalkOptions;
92
93 StackWalker(
94 int options = OptionsAll, // 'int' is by design, to combine the enum-flags
95 LPCSTR szSymPath = NULL,
96 DWORD dwProcessId = GetCurrentProcessId(),
97 HANDLE hProcess = GetCurrentProcess()
98 );
99 StackWalker(DWORD dwProcessId, HANDLE hProcess);
100 virtual ~StackWalker();
101
102 typedef BOOL (__stdcall *PReadProcessMemoryRoutine)(
103 HANDLE hProcess,
104 DWORD64 qwBaseAddress,
105 PVOID lpBuffer,
106 DWORD nSize,
107 LPDWORD lpNumberOfBytesRead,
108 LPVOID pUserData // optional data, which was passed in "ShowCallstack"
109 );
110
111 BOOL LoadModules();
112
113 BOOL ShowCallstack(
114 HANDLE hThread = GetCurrentThread(),
115 const CONTEXT *context = NULL,
116 PReadProcessMemoryRoutine readMemoryFunction = NULL,
117 LPVOID pUserData = NULL // optional to identify some data in the 'readMemoryFunction'-callback
118 );
119
120 #if _MSC_VER >= 1300
121 // due to some reasons, the "STACKWALK_MAX_NAMELEN" must be declared as "public"
122 // in older compilers in order to use it... starting with VC7 we can declare it as "protected"
123 protected:
124 #endif
125 enum { STACKWALK_MAX_NAMELEN = 1024 }; // max name length for found symbols
126
127 protected:
128 // Entry for each Callstack-Entry
129 typedef struct CallstackEntry
130 {
131 DWORD64 offset; // if 0, we have no valid entry
132 CHAR name[STACKWALK_MAX_NAMELEN];
133 CHAR undName[STACKWALK_MAX_NAMELEN];
134 CHAR undFullName[STACKWALK_MAX_NAMELEN];
135 DWORD64 offsetFromSmybol;
136 DWORD offsetFromLine;
137 DWORD lineNumber;
138 CHAR lineFileName[STACKWALK_MAX_NAMELEN];
139 DWORD symType;
140 LPCSTR symTypeString;
141 CHAR moduleName[STACKWALK_MAX_NAMELEN];
142 DWORD64 baseOfImage;
143 CHAR loadedImageName[STACKWALK_MAX_NAMELEN];
144 } CallstackEntry;
145
146 typedef enum CallstackEntryType {firstEntry, nextEntry, lastEntry};
147
148 virtual void OnSymInit(LPCSTR szSearchPath, DWORD symOptions, LPCSTR szUserName);
149 virtual void OnLoadModule(LPCSTR img, LPCSTR mod, DWORD64 baseAddr, DWORD size, DWORD result, LPCSTR symType, LPCSTR pdbName, ULONGLONG fileVersion);
150 virtual void OnCallstackEntry(CallstackEntryType eType, CallstackEntry &entry);
151 virtual void OnDbgHelpErr(LPCSTR szFuncName, DWORD gle, DWORD64 addr);
152 virtual void OnOutput(LPCSTR szText);
153
154 StackWalkerInternal *m_sw;
155 HANDLE m_hProcess;
156 DWORD m_dwProcessId;
157 BOOL m_modulesLoaded;
158 LPSTR m_szSymPath;
159
160 int m_options;
161 int m_MaxRecursionCount;
162
163 static BOOL __stdcall myReadProcMem(HANDLE hProcess, DWORD64 qwBaseAddress, PVOID lpBuffer, DWORD nSize, LPDWORD lpNumberOfBytesRead);
164
165 friend StackWalkerInternal;
166 }; // class StackWalker
167
168
169 // The "ugly" assembler-implementation is needed for systems before XP
170 // If you have a new PSDK and you only compile for XP and later, then you can use
171 // the "RtlCaptureContext"
172 // Currently there is no define which determines the PSDK-Version...
173 // So we just use the compiler-version (and assumes that the PSDK is
174 // the one which was installed by the VS-IDE)
175
176 // INFO: If you want, you can use the RtlCaptureContext if you only target XP and later...
177 // But I currently use it in x64/IA64 environments...
178 //#if defined(_M_IX86) && (_WIN32_WINNT <= 0x0500) && (_MSC_VER < 1400)
179
180 #if defined(_M_IX86)
181 #ifdef CURRENT_THREAD_VIA_EXCEPTION
182 // TODO: The following is not a "good" implementation,
183 // because the callstack is only valid in the "__except" block...
184 #define GET_CURRENT_CONTEXT_STACKWALKER_CODEPLEX(c, contextFlags) \
185 do { \
186 memset(&c, 0, sizeof(CONTEXT)); \
187 EXCEPTION_POINTERS *pExp = NULL; \
188 __try { \
189 throw 0; \
190 } __except( ( (pExp = GetExceptionInformation()) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_EXECUTE_HANDLER)) {} \
191 if (pExp != NULL) \
192 memcpy(&c, pExp->ContextRecord, sizeof(CONTEXT)); \
193 c.ContextFlags = contextFlags; \
194 } while(0);
195 #else
196 // The following should be enough for walking the callstack...
197 #define GET_CURRENT_CONTEXT_STACKWALKER_CODEPLEX(c, contextFlags) \
198 do { \
199 memset(&c, 0, sizeof(CONTEXT)); \
200 c.ContextFlags = contextFlags; \
201 __asm call x \
202 __asm x: pop eax \
203 __asm mov c.Eip, eax \
204 __asm mov c.Ebp, ebp \
205 __asm mov c.Esp, esp \
206 } while(0);
207 #endif
208
209 #else
210
211 // The following is defined for x86 (XP and higher), x64 and IA64:
212 #define GET_CURRENT_CONTEXT_STACKWALKER_CODEPLEX(c, contextFlags) \
213 do { \
214 memset(&c, 0, sizeof(CONTEXT)); \
215 c.ContextFlags = contextFlags; \
216 RtlCaptureContext(&c); \
217 } while(0);
218 #endif
StackWalkerInternal
Definition: StackWalker.cc:253
StackWalker::CallstackEntry
Definition: StackWalker.hh:129
StackWalker
Definition: StackWalker.hh:56