1 // Modifications for WZ:
2 //	2019-03-25 - Modified to always use RtlCaptureContext when possible
3 
4 #ifndef __STACKWALKER_H__
5 #define __STACKWALKER_H__
6 
7 #if defined(_MSC_VER)
8 
9 /**********************************************************************
10  *
11  * StackWalker.h
12  *
13  *
14  *
15  * LICENSE (http://www.opensource.org/licenses/bsd-license.php)
16  *
17  *   Copyright (c) 2005-2009, Jochen Kalmbach
18  *   All rights reserved.
19  *
20  *   Redistribution and use in source and binary forms, with or without modification,
21  *   are permitted provided that the following conditions are met:
22  *
23  *   Redistributions of source code must retain the above copyright notice,
24  *   this list of conditions and the following disclaimer.
25  *   Redistributions in binary form must reproduce the above copyright notice,
26  *   this list of conditions and the following disclaimer in the documentation
27  *   and/or other materials provided with the distribution.
28  *   Neither the name of Jochen Kalmbach nor the names of its contributors may be
29  *   used to endorse or promote products derived from this software without
30  *   specific prior written permission.
31  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
32  *   AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
33  *   THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
34  *   ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
35  *   FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
36  *   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
37  *   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
38  *   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
39  *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
40  *   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
41  *
42  * **********************************************************************/
43 // #pragma once is supported starting with _MSC_VER 1000,
44 // so we need not to check the version (because we only support _MSC_VER >= 1100)!
45 #pragma once
46 
47 #include <windows.h>
48 
49 #if _MSC_VER >= 1900
50 #pragma warning(disable : 4091)
51 #endif
52 
53 // special defines for VC5/6 (if no actual PSDK is installed):
54 #if _MSC_VER < 1300
55 typedef unsigned __int64 DWORD64, *PDWORD64;
56 #if defined(_WIN64)
57 typedef unsigned __int64 SIZE_T, *PSIZE_T;
58 #else
59 typedef unsigned long SIZE_T, *PSIZE_T;
60 #endif
61 #endif // _MSC_VER < 1300
62 
63 class StackWalkerInternal; // forward
64 class StackWalker
65 {
66 public:
67   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 
101   StackWalker(int    options = OptionsAll, // 'int' is by design, to combine the enum-flags
102               LPCSTR szSymPath = NULL,
103               DWORD  dwProcessId = GetCurrentProcessId(),
104               HANDLE hProcess = GetCurrentProcess());
105   StackWalker(DWORD dwProcessId, HANDLE hProcess);
106   virtual ~StackWalker();
107 
108   typedef BOOL(__stdcall* PReadProcessMemoryRoutine)(
109       HANDLE  hProcess,
110       DWORD64 qwBaseAddress,
111       PVOID   lpBuffer,
112       DWORD   nSize,
113       LPDWORD lpNumberOfBytesRead,
114       LPVOID  pUserData // optional data, which was passed in "ShowCallstack"
115   );
116 
117   BOOL LoadModules();
118 
119   BOOL ShowCallstack(
120       HANDLE                    hThread = GetCurrentThread(),
121       const CONTEXT*            context = NULL,
122       PReadProcessMemoryRoutine readMemoryFunction = NULL,
123       LPVOID pUserData = NULL // optional to identify some data in the 'readMemoryFunction'-callback
124   );
125 
126   BOOL ShowObject(LPVOID pObject);
127 
128 #if _MSC_VER >= 1300
129   // due to some reasons, the "STACKWALK_MAX_NAMELEN" must be declared as "public"
130   // in older compilers in order to use it... starting with VC7 we can declare it as "protected"
131 protected:
132 #endif
133   enum
134   {
135     STACKWALK_MAX_NAMELEN = 1024
136   }; // max name length for found symbols
137 
138 protected:
139   // Entry for each Callstack-Entry
140   typedef struct CallstackEntry
141   {
142     DWORD64 offset; // if 0, we have no valid entry
143     CHAR    name[STACKWALK_MAX_NAMELEN];
144     CHAR    undName[STACKWALK_MAX_NAMELEN];
145     CHAR    undFullName[STACKWALK_MAX_NAMELEN];
146     DWORD64 offsetFromSmybol;
147     DWORD   offsetFromLine;
148     DWORD   lineNumber;
149     CHAR    lineFileName[STACKWALK_MAX_NAMELEN];
150     DWORD   symType;
151     LPCSTR  symTypeString;
152     CHAR    moduleName[STACKWALK_MAX_NAMELEN];
153     DWORD64 baseOfImage;
154     CHAR    loadedImageName[STACKWALK_MAX_NAMELEN];
155   } CallstackEntry;
156 
157   typedef enum CallstackEntryType
158   {
159     firstEntry,
160     nextEntry,
161     lastEntry
162   } CallstackEntryType;
163 
164   virtual void OnSymInit(LPCSTR szSearchPath, DWORD symOptions, LPCSTR szUserName);
165   virtual void OnLoadModule(LPCSTR    img,
166                             LPCSTR    mod,
167                             DWORD64   baseAddr,
168                             DWORD     size,
169                             DWORD     result,
170                             LPCSTR    symType,
171                             LPCSTR    pdbName,
172                             ULONGLONG fileVersion);
173   virtual void OnCallstackEntry(CallstackEntryType eType, CallstackEntry& entry);
174   virtual void OnDbgHelpErr(LPCSTR szFuncName, DWORD gle, DWORD64 addr);
175   virtual void OnOutput(LPCSTR szText);
176 
177   virtual void BeforeLoadModules(); // WZ
178   virtual void BeforeShowCallstack(); // WZ
179 
180   StackWalkerInternal* m_sw;
181   HANDLE               m_hProcess;
182   DWORD                m_dwProcessId;
183   BOOL                 m_modulesLoaded;
184   LPSTR                m_szSymPath;
185 
186   int m_options;
187   int m_MaxRecursionCount;
188 
189   static BOOL __stdcall myReadProcMem(HANDLE  hProcess,
190                                       DWORD64 qwBaseAddress,
191                                       PVOID   lpBuffer,
192                                       DWORD   nSize,
193                                       LPDWORD lpNumberOfBytesRead);
194 
195   friend StackWalkerInternal;
196 }; // class StackWalker
197 
198 // The "ugly" assembler-implementation is needed for systems before XP
199 // If you have a new PSDK and you only compile for XP and later, then you can use
200 // the "RtlCaptureContext"
201 // Currently there is no define which determines the PSDK-Version...
202 // So we just use the compiler-version (and assumes that the PSDK is
203 // the one which was installed by the VS-IDE)
204 
205 // INFO: If you want, you can use the RtlCaptureContext if you only target XP and later...
206 //       But I currently use it in x64/IA64 environments...
207 //#if defined(_M_IX86) && (_WIN32_WINNT <= 0x0500) && (_MSC_VER < 1400)
208 
209 #if defined(_M_IX86) && (_WIN32_WINNT <= 0x0500) && (_MSC_VER < 1400) //#if defined(_M_IX86) // WZ
210 #ifdef CURRENT_THREAD_VIA_EXCEPTION
211 // TODO: The following is not a "good" implementation,
212 // because the callstack is only valid in the "__except" block...
213 #define GET_CURRENT_CONTEXT_STACKWALKER_CODEPLEX(c, contextFlags)               \
214   do                                                                            \
215   {                                                                             \
216     memset(&c, 0, sizeof(CONTEXT));                                             \
217     EXCEPTION_POINTERS* pExp = NULL;                                            \
218     __try                                                                       \
219     {                                                                           \
220       throw 0;                                                                  \
221     }                                                                           \
222     __except (((pExp = GetExceptionInformation()) ? EXCEPTION_EXECUTE_HANDLER   \
223                                                   : EXCEPTION_EXECUTE_HANDLER)) \
224     {                                                                           \
225     }                                                                           \
226     if (pExp != NULL)                                                           \
227       memcpy(&c, pExp->ContextRecord, sizeof(CONTEXT));                         \
228     c.ContextFlags = contextFlags;                                              \
229   } while (0);
230 #else
231 // clang-format off
232 // The following should be enough for walking the callstack...
233 #define GET_CURRENT_CONTEXT_STACKWALKER_CODEPLEX(c, contextFlags) \
234   do                                                              \
235   {                                                               \
236     memset(&c, 0, sizeof(CONTEXT));                               \
237     c.ContextFlags = contextFlags;                                \
238     __asm    call x                                               \
239     __asm x: pop eax                                              \
240     __asm    mov c.Eip, eax                                       \
241     __asm    mov c.Ebp, ebp                                       \
242     __asm    mov c.Esp, esp                                       \
243   } while (0)
244 // clang-format on
245 #endif
246 
247 #else
248 
249 // The following is defined for x86 (XP and higher), x64 and IA64:
250 #define GET_CURRENT_CONTEXT_STACKWALKER_CODEPLEX(c, contextFlags) \
251   do                                                              \
252   {                                                               \
253     memset(&c, 0, sizeof(CONTEXT));                               \
254     c.ContextFlags = contextFlags;                                \
255     RtlCaptureContext(&c);                                        \
256   } while (0);
257 #endif
258 
259 #endif //defined(_MSC_VER)
260 
261 #endif // __STACKWALKER_H__
262