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