Btk
StackWalker.h
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__
Definition: StackWalker.cpp:249