RCFProto
 All Classes Functions Typedefs
StackWalker.h
1 /**********************************************************************
2  *
3  * StackWalker.h
4  *
5  *
6  * History:
7  * 2005-07-27 v1 - First public release on http://www.codeproject.com/
8  * (for additional changes see History in 'StackWalker.cpp'!
9  *
10  **********************************************************************/
11 // #pragma once is supported starting with _MCS_VER 1000,
12 // so we need not to check the version (because we only support _MSC_VER >= 1100)!
13 #pragma once
14 
15 #include <windows.h>
16 
17 // special defines for VC5/6 (if no actual PSDK is installed):
18 #if _MSC_VER < 1300
19 typedef unsigned __int64 DWORD64, *PDWORD64;
20 #if defined(_WIN64)
21 typedef unsigned __int64 SIZE_T, *PSIZE_T;
22 #else
23 typedef unsigned long SIZE_T, *PSIZE_T;
24 #endif
25 #endif // _MSC_VER < 1300
26 
27 class StackWalkerInternal; // forward
28 class StackWalker
29 {
30 public:
31  typedef enum StackWalkOptions
32  {
33  // No addition info will be retrived
34  // (only the address is available)
35  RetrieveNone = 0,
36 
37  // Try to get the symbol-name
38  RetrieveSymbol = 1,
39 
40  // Try to get the line for this symbol
41  RetrieveLine = 2,
42 
43  // Try to retrieve the module-infos
44  RetrieveModuleInfo = 4,
45 
46  // Also retrieve the version for the DLL/EXE
47  RetrieveFileVersion = 8,
48 
49  // Contains all the abouve
50  RetrieveVerbose = 0xF,
51 
52  // Generate a "good" symbol-search-path
53  SymBuildPath = 0x10,
54 
55  // Also use the public Microsoft-Symbol-Server
56  SymUseSymSrv = 0x20,
57 
58  // Contains all the abouve "Sym"-options
59  SymAll = 0x30,
60 
61  // Contains all options (default)
62  OptionsAll = 0x3F
63  } StackWalkOptions;
64 
65  StackWalker(
66  int options = OptionsAll, // 'int' is by design, to combine the enum-flags
67  LPCSTR szSymPath = NULL,
68  DWORD dwProcessId = GetCurrentProcessId(),
69  HANDLE hProcess = GetCurrentProcess()
70  );
71  StackWalker(DWORD dwProcessId, HANDLE hProcess);
72  virtual ~StackWalker();
73 
74  typedef BOOL (__stdcall *PReadProcessMemoryRoutine)(
75  HANDLE hProcess,
76  DWORD64 qwBaseAddress,
77  PVOID lpBuffer,
78  DWORD nSize,
79  LPDWORD lpNumberOfBytesRead,
80  LPVOID pUserData // optional data, which was passed in "ShowCallstack"
81  );
82 
83  BOOL LoadModules();
84 
85  BOOL ShowCallstack(
86  HANDLE hThread = GetCurrentThread(),
87  const CONTEXT *context = NULL,
88  PReadProcessMemoryRoutine readMemoryFunction = NULL,
89  LPVOID pUserData = NULL // optional to identify some data in the 'readMemoryFunction'-callback
90  );
91 
92 #if _MSC_VER >= 1300
93 // due to some reasons, the "STACKWALK_MAX_NAMELEN" must be declared as "public"
94 // in older compilers in order to use it... starting with VC7 we can declare it as "protected"
95 protected:
96 #endif
97  enum { STACKWALK_MAX_NAMELEN = 1024 }; // max name length for found symbols
98 
99 protected:
100  // Entry for each Callstack-Entry
101  typedef struct CallstackEntry
102  {
103  DWORD64 offset; // if 0, we have no valid entry
104  CHAR name[STACKWALK_MAX_NAMELEN];
105  CHAR undName[STACKWALK_MAX_NAMELEN];
106  CHAR undFullName[STACKWALK_MAX_NAMELEN];
107  DWORD64 offsetFromSmybol;
108  DWORD offsetFromLine;
109  DWORD lineNumber;
110  CHAR lineFileName[STACKWALK_MAX_NAMELEN];
111  DWORD symType;
112  LPCSTR symTypeString;
113  CHAR moduleName[STACKWALK_MAX_NAMELEN];
114  DWORD64 baseOfImage;
115  CHAR loadedImageName[STACKWALK_MAX_NAMELEN];
116  } CallstackEntry;
117 
118  typedef enum CallstackEntryType {firstEntry, nextEntry, lastEntry};
119 
120  virtual void OnSymInit(LPCSTR szSearchPath, DWORD symOptions, LPCSTR szUserName);
121  virtual void OnLoadModule(LPCSTR img, LPCSTR mod, DWORD64 baseAddr, DWORD size, DWORD result, LPCSTR symType, LPCSTR pdbName, ULONGLONG fileVersion);
122  virtual void OnCallstackEntry(CallstackEntryType eType, CallstackEntry &entry);
123  virtual void OnDbgHelpErr(LPCSTR szFuncName, DWORD gle, DWORD64 addr);
124  virtual void OnOutput(LPCSTR szText);
125 
126  StackWalkerInternal *m_sw;
127  HANDLE m_hProcess;
128  DWORD m_dwProcessId;
129  BOOL m_modulesLoaded;
130  LPSTR m_szSymPath;
131 
132  int m_options;
133 
134  static BOOL __stdcall myReadProcMem(HANDLE hProcess, DWORD64 qwBaseAddress, PVOID lpBuffer, DWORD nSize, LPDWORD lpNumberOfBytesRead);
135 
136  friend StackWalkerInternal;
137 };
138 
139 
140 // The "ugly" assembler-implementation is needed for systems before XP
141 // If you have a new PSDK and you only compile for XP and later, then you can use
142 // the "RtlCaptureContext"
143 // Currently there is no define which determines the PSDK-Version...
144 // So we just use the compiler-version (and assumes that the PSDK is
145 // the one which was installed by the VS-IDE)
146 
147 // INFO: If you want, you can use the RtlCaptureContext if you only target XP and later...
148 // But I currently use it in x64/IA64 environments...
149 //#if defined(_M_IX86) && (_WIN32_WINNT <= 0x0500) && (_MSC_VER < 1400)
150 
151 #if defined(_M_IX86)
152 #ifdef CURRENT_THREAD_VIA_EXCEPTION
153 // TODO: The following is not a "good" implementation,
154 // because the callstack is only valid in the "__except" block...
155 #define GET_CURRENT_CONTEXT(c, contextFlags) \
156  do { \
157  memset(&c, 0, sizeof(CONTEXT)); \
158  EXCEPTION_POINTERS *pExp = NULL; \
159  __try { \
160  throw 0; \
161  } __except( ( (pExp = GetExceptionInformation()) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_EXECUTE_HANDLER)) {} \
162  if (pExp != NULL) \
163  memcpy(&c, pExp->ContextRecord, sizeof(CONTEXT)); \
164  c.ContextFlags = contextFlags; \
165  } while(0);
166 #else
167 // The following should be enough for walking the callstack...
168 #define GET_CURRENT_CONTEXT(c, contextFlags) \
169  do { \
170  memset(&c, 0, sizeof(CONTEXT)); \
171  c.ContextFlags = contextFlags; \
172  __asm call x \
173  __asm x: pop eax \
174  __asm mov c.Eip, eax \
175  __asm mov c.Ebp, ebp \
176  __asm mov c.Esp, esp \
177  } while(0);
178 #endif
179 
180 #else
181 
182 // The following is defined for x86 (XP and higher), x64 and IA64:
183 #define GET_CURRENT_CONTEXT(c, contextFlags) \
184  do { \
185  memset(&c, 0, sizeof(CONTEXT)); \
186  c.ContextFlags = contextFlags; \
187  RtlCaptureContext(&c); \
188 } while(0);
189 #endif
190 
191 class StackTrace : public StackWalker
192 {
193 public:
194  StackTrace() : StackWalker()
195  {
196  ShowCallstack();
197  }
198 
199  const std::string & toString() const
200  {
201  return mOutput;
202  }
203 
204 protected:
205  virtual void OnOutput(LPCSTR szText)
206  {
207  mOutput += szText;
208  }
209 
210  virtual void OnSymInit(LPCSTR szSearchPath, DWORD symOptions, LPCSTR szUserName)
211  {
212  }
213 
214  virtual void OnLoadModule(LPCSTR img, LPCSTR mod, DWORD64 baseAddr, DWORD size,
215  DWORD result, LPCSTR symType, LPCSTR pdbName, ULONGLONG fileVersion)
216  {
217  }
218 
219  //virtual void OnCallstackEntry(CallstackEntryType eType, CallstackEntry &entry)
220  //{
221  // mOutput += entry.;
222  //}
223 
224  virtual void OnDbgHelpErr(LPCSTR szFuncName, DWORD gle, DWORD64 addr)
225  {
226  }
227 
228  std::string mOutput;
229 };