blob: 5ab241ec1396fe82d432bf0c145382d318559d63 [file] [log] [blame]
James Kuszmaulcf324122023-01-14 14:07:17 -08001#ifndef __STACKWALKER_H__
2#define __STACKWALKER_H__
3
4#if defined(_MSC_VER)
5
Brian Silverman8fce7482020-01-05 13:18:21 -08006/**********************************************************************
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 * **********************************************************************/
James Kuszmaulcf324122023-01-14 14:07:17 -080040// #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)!
Brian Silverman8fce7482020-01-05 13:18:21 -080042#pragma once
43
44#include <windows.h>
45
James Kuszmaulb13e13f2023-11-22 20:44:04 -080046// special defines for VC5/6 (if no actual PSDK is installed):
47#if _MSC_VER < 1300
48typedef unsigned __int64 DWORD64, *PDWORD64;
49#if defined(_WIN64)
50typedef unsigned __int64 SIZE_T, *PSIZE_T;
51#else
52typedef unsigned long SIZE_T, *PSIZE_T;
Brian Silverman8fce7482020-01-05 13:18:21 -080053#endif
James Kuszmaulb13e13f2023-11-22 20:44:04 -080054#endif // _MSC_VER < 1300
Brian Silverman8fce7482020-01-05 13:18:21 -080055
56class StackWalkerInternal; // forward
57class StackWalker
58{
59public:
James Kuszmaulb13e13f2023-11-22 20:44:04 -080060 typedef enum ExceptType
61 {
62 NonExcept = 0, // RtlCaptureContext
63 AfterExcept = 1,
64 AfterCatch = 2, // get_current_exception_context
65 } ExceptType;
66
Brian Silverman8fce7482020-01-05 13:18:21 -080067 typedef enum StackWalkOptions
68 {
69 // No addition info will be retrieved
70 // (only the address is available)
71 RetrieveNone = 0,
72
73 // Try to get the symbol-name
74 RetrieveSymbol = 1,
75
76 // Try to get the line for this symbol
77 RetrieveLine = 2,
78
79 // Try to retrieve the module-infos
80 RetrieveModuleInfo = 4,
81
82 // Also retrieve the version for the DLL/EXE
83 RetrieveFileVersion = 8,
84
85 // Contains all the above
86 RetrieveVerbose = 0xF,
87
88 // Generate a "good" symbol-search-path
89 SymBuildPath = 0x10,
90
91 // Also use the public Microsoft-Symbol-Server
92 SymUseSymSrv = 0x20,
93
94 // Contains all the above "Sym"-options
95 SymAll = 0x30,
96
97 // Contains all options (default)
98 OptionsAll = 0x3F
99 } StackWalkOptions;
100
James Kuszmaulb13e13f2023-11-22 20:44:04 -0800101 StackWalker(ExceptType extype, int options = OptionsAll, PEXCEPTION_POINTERS exp = NULL);
102
Brian Silverman8fce7482020-01-05 13:18:21 -0800103 StackWalker(int options = OptionsAll, // 'int' is by design, to combine the enum-flags
James Kuszmaulb13e13f2023-11-22 20:44:04 -0800104 LPCSTR szSymPath = NULL,
Brian Silverman8fce7482020-01-05 13:18:21 -0800105 DWORD dwProcessId = GetCurrentProcessId(),
106 HANDLE hProcess = GetCurrentProcess());
James Kuszmaulb13e13f2023-11-22 20:44:04 -0800107
Brian Silverman8fce7482020-01-05 13:18:21 -0800108 StackWalker(DWORD dwProcessId, HANDLE hProcess);
James Kuszmaulb13e13f2023-11-22 20:44:04 -0800109
Brian Silverman8fce7482020-01-05 13:18:21 -0800110 virtual ~StackWalker();
111
James Kuszmaulb13e13f2023-11-22 20:44:04 -0800112 bool SetSymPath(LPCSTR szSymPath);
113
114 bool SetTargetProcess(DWORD dwProcessId, HANDLE hProcess);
115
116 PCONTEXT GetCurrentExceptionContext();
117
118private:
119 bool Init(ExceptType extype, int options, LPCSTR szSymPath, DWORD dwProcessId,
120 HANDLE hProcess, PEXCEPTION_POINTERS exp = NULL);
121
122public:
Brian Silverman8fce7482020-01-05 13:18:21 -0800123 typedef BOOL(__stdcall* PReadProcessMemoryRoutine)(
124 HANDLE hProcess,
125 DWORD64 qwBaseAddress,
126 PVOID lpBuffer,
127 DWORD nSize,
128 LPDWORD lpNumberOfBytesRead,
129 LPVOID pUserData // optional data, which was passed in "ShowCallstack"
130 );
131
132 BOOL LoadModules();
133
134 BOOL ShowCallstack(
135 HANDLE hThread = GetCurrentThread(),
136 const CONTEXT* context = NULL,
137 PReadProcessMemoryRoutine readMemoryFunction = NULL,
138 LPVOID pUserData = NULL // optional to identify some data in the 'readMemoryFunction'-callback
139 );
140
141 BOOL ShowObject(LPVOID pObject);
142
James Kuszmaulcf324122023-01-14 14:07:17 -0800143#if _MSC_VER >= 1300
144 // due to some reasons, the "STACKWALK_MAX_NAMELEN" must be declared as "public"
145 // in older compilers in order to use it... starting with VC7 we can declare it as "protected"
Brian Silverman8fce7482020-01-05 13:18:21 -0800146protected:
James Kuszmaulcf324122023-01-14 14:07:17 -0800147#endif
Brian Silverman8fce7482020-01-05 13:18:21 -0800148 enum
149 {
150 STACKWALK_MAX_NAMELEN = 1024
151 }; // max name length for found symbols
152
153protected:
154 // Entry for each Callstack-Entry
155 typedef struct CallstackEntry
156 {
157 DWORD64 offset; // if 0, we have no valid entry
James Kuszmaulb13e13f2023-11-22 20:44:04 -0800158 CHAR name[STACKWALK_MAX_NAMELEN];
159 CHAR undName[STACKWALK_MAX_NAMELEN];
160 CHAR undFullName[STACKWALK_MAX_NAMELEN];
Brian Silverman8fce7482020-01-05 13:18:21 -0800161 DWORD64 offsetFromSmybol;
162 DWORD offsetFromLine;
163 DWORD lineNumber;
James Kuszmaulb13e13f2023-11-22 20:44:04 -0800164 CHAR lineFileName[STACKWALK_MAX_NAMELEN];
Brian Silverman8fce7482020-01-05 13:18:21 -0800165 DWORD symType;
166 LPCSTR symTypeString;
James Kuszmaulb13e13f2023-11-22 20:44:04 -0800167 CHAR moduleName[STACKWALK_MAX_NAMELEN];
Brian Silverman8fce7482020-01-05 13:18:21 -0800168 DWORD64 baseOfImage;
James Kuszmaulb13e13f2023-11-22 20:44:04 -0800169 CHAR loadedImageName[STACKWALK_MAX_NAMELEN];
Brian Silverman8fce7482020-01-05 13:18:21 -0800170 } CallstackEntry;
171
172 typedef enum CallstackEntryType
173 {
174 firstEntry,
175 nextEntry,
176 lastEntry
177 } CallstackEntryType;
178
James Kuszmaulb13e13f2023-11-22 20:44:04 -0800179 virtual void OnSymInit(LPCSTR szSearchPath, DWORD symOptions, LPCSTR szUserName);
180 virtual void OnLoadModule(LPCSTR img,
181 LPCSTR mod,
Brian Silverman8fce7482020-01-05 13:18:21 -0800182 DWORD64 baseAddr,
183 DWORD size,
184 DWORD result,
James Kuszmaulb13e13f2023-11-22 20:44:04 -0800185 LPCSTR symType,
186 LPCSTR pdbName,
Brian Silverman8fce7482020-01-05 13:18:21 -0800187 ULONGLONG fileVersion);
188 virtual void OnCallstackEntry(CallstackEntryType eType, CallstackEntry& entry);
James Kuszmaulb13e13f2023-11-22 20:44:04 -0800189 virtual void OnDbgHelpErr(LPCSTR szFuncName, DWORD gle, DWORD64 addr);
190 virtual void OnOutput(LPCSTR szText);
Brian Silverman8fce7482020-01-05 13:18:21 -0800191
192 StackWalkerInternal* m_sw;
193 HANDLE m_hProcess;
194 DWORD m_dwProcessId;
195 BOOL m_modulesLoaded;
James Kuszmaulb13e13f2023-11-22 20:44:04 -0800196 LPSTR m_szSymPath;
Brian Silverman8fce7482020-01-05 13:18:21 -0800197
198 int m_options;
199 int m_MaxRecursionCount;
200
201 static BOOL __stdcall myReadProcMem(HANDLE hProcess,
202 DWORD64 qwBaseAddress,
203 PVOID lpBuffer,
204 DWORD nSize,
205 LPDWORD lpNumberOfBytesRead);
206
207 friend StackWalkerInternal;
208}; // class StackWalker
209
James Kuszmaulb13e13f2023-11-22 20:44:04 -0800210// The "ugly" assembler-implementation is needed for systems before XP
211// If you have a new PSDK and you only compile for XP and later, then you can use
212// the "RtlCaptureContext"
213// Currently there is no define which determines the PSDK-Version...
214// So we just use the compiler-version (and assumes that the PSDK is
215// the one which was installed by the VS-IDE)
216
217// INFO: If you want, you can use the RtlCaptureContext if you only target XP and later...
218// But I currently use it in x64/IA64 environments...
219//#if defined(_M_IX86) && (_WIN32_WINNT <= 0x0500) && (_MSC_VER < 1400)
220
221#if defined(_M_IX86)
222#ifdef CURRENT_THREAD_VIA_EXCEPTION
223// TODO: The following is not a "good" implementation,
224// because the callstack is only valid in the "__except" block...
225#define GET_CURRENT_CONTEXT_STACKWALKER_CODEPLEX(c, contextFlags) \
226 do \
227 { \
228 memset(&c, 0, sizeof(CONTEXT)); \
229 EXCEPTION_POINTERS* pExp = NULL; \
230 __try \
231 { \
232 throw 0; \
233 } \
234 __except (((pExp = GetExceptionInformation()) ? EXCEPTION_EXECUTE_HANDLER \
235 : EXCEPTION_EXECUTE_HANDLER)) \
236 { \
237 } \
238 if (pExp != NULL) \
239 memcpy(&c, pExp->ContextRecord, sizeof(CONTEXT)); \
240 c.ContextFlags = contextFlags; \
241 } while (0);
242#else
243// clang-format off
244// The following should be enough for walking the callstack...
245#define GET_CURRENT_CONTEXT_STACKWALKER_CODEPLEX(c, contextFlags) \
246 do \
247 { \
248 memset(&c, 0, sizeof(CONTEXT)); \
249 c.ContextFlags = contextFlags; \
250 __asm call x \
251 __asm x: pop eax \
252 __asm mov c.Eip, eax \
253 __asm mov c.Ebp, ebp \
254 __asm mov c.Esp, esp \
255 } while (0)
256// clang-format on
257#endif
258
259#else
260
Brian Silverman8fce7482020-01-05 13:18:21 -0800261// The following is defined for x86 (XP and higher), x64 and IA64:
262#define GET_CURRENT_CONTEXT_STACKWALKER_CODEPLEX(c, contextFlags) \
263 do \
264 { \
265 memset(&c, 0, sizeof(CONTEXT)); \
266 c.ContextFlags = contextFlags; \
267 RtlCaptureContext(&c); \
268 } while (0);
James Kuszmaulb13e13f2023-11-22 20:44:04 -0800269#endif
James Kuszmaulcf324122023-01-14 14:07:17 -0800270
271#endif //defined(_MSC_VER)
272
273#endif // __STACKWALKER_H__