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__
|
---|