1 /**********************************************************************
\r
7 * 2005-07-27 v1 - First public release on http://www.codeproject.com/
\r
8 * (for additional changes see History in 'stack_walker.cpp'!
\r
10 **********************************************************************/
\r
11 // #pragma once is supported starting with _MCS_VER 1000,
\r
12 // so we need not to check the version (because we only support _MSC_VER >= 1100)!
\r
15 #include <windows.h>
\r
17 // special defines for VC5/6 (if no actual PSDK is installed):
\r
19 typedef unsigned __int64 DWORD64, *PDWORD64;
\r
21 typedef unsigned __int64 SIZE_T, *PSIZE_T;
\r
23 typedef unsigned long SIZE_T, *PSIZE_T;
\r
25 #endif // _MSC_VER < 1300
\r
27 class stack_walkerInternal; // forward
\r
31 typedef enum StackWalkOptions
\r
33 // No addition info will be retrived
\r
34 // (only the address is available)
\r
37 // Try to get the symbol-name
\r
40 // Try to get the line for this symbol
\r
43 // Try to retrieve the module-infos
\r
44 RetrieveModuleInfo = 4,
\r
46 // Also retrieve the version for the DLL/EXE
\r
47 RetrieveFileVersion = 8,
\r
49 // Contains all the abouve
\r
50 RetrieveVerbose = 0xF,
\r
52 // Generate a "good" symbol-search-path
\r
53 SymBuildPath = 0x10,
\r
55 // Also use the public Microsoft-Symbol-Server
\r
56 SymUseSymSrv = 0x20,
\r
58 // Contains all the abouve "Sym"-options
\r
61 // Contains all options (default)
\r
66 int options = OptionsAll, // 'int' is by design, to combine the enum-flags
\r
67 LPCSTR szSymPath = NULL,
\r
68 DWORD dwProcessId = GetCurrentProcessId(),
\r
69 HANDLE hProcess = GetCurrentProcess()
\r
71 stack_walker(DWORD dwProcessId, HANDLE hProcess);
\r
72 virtual ~stack_walker();
\r
74 typedef BOOL (__stdcall *PReadProcessMemoryRoutine)(
\r
76 DWORD64 qwBaseAddress,
\r
79 LPDWORD lpNumberOfBytesRead,
\r
80 LPVOID pUserData // optional data, which was passed in "ShowCallstack"
\r
86 HANDLE hThread = GetCurrentThread(),
\r
87 const CONTEXT *context = NULL,
\r
88 PReadProcessMemoryRoutine readMemoryFunction = NULL,
\r
89 LPVOID pUserData = NULL // optional to identify some data in the 'readMemoryFunction'-callback
\r
92 #if _MSC_VER >= 1300
\r
93 // due to some reasons, the "STACKWALK_MAX_NAMELEN" must be declared as "public"
\r
94 // in older compilers in order to use it... starting with VC7 we can declare it as "protected"
\r
97 enum { STACKWALK_MAX_NAMELEN = 1024 }; // max name length for found symbols
\r
100 // Entry for each Callstack-Entry
\r
101 typedef struct CallstackEntry
\r
103 DWORD64 offset; // if 0, we have no valid entry
\r
104 CHAR name[STACKWALK_MAX_NAMELEN];
\r
105 CHAR undName[STACKWALK_MAX_NAMELEN];
\r
106 CHAR undFullName[STACKWALK_MAX_NAMELEN];
\r
107 DWORD64 offsetFromSmybol;
\r
108 DWORD offsetFromLine;
\r
110 CHAR lineFileName[STACKWALK_MAX_NAMELEN];
\r
112 LPCSTR symTypeString;
\r
113 CHAR moduleName[STACKWALK_MAX_NAMELEN];
\r
114 DWORD64 baseOfImage;
\r
115 CHAR loadedImageName[STACKWALK_MAX_NAMELEN];
\r
118 typedef enum CallstackEntryType {firstEntry, nextEntry, lastEntry};
\r
120 virtual void OnSymInit(LPCSTR szSearchPath, DWORD symOptions, LPCSTR szUserName);
\r
121 virtual void OnLoadModule(LPCSTR img, LPCSTR mod, DWORD64 baseAddr, DWORD size, DWORD result, LPCSTR symType, LPCSTR pdbName, ULONGLONG fileVersion);
\r
122 virtual void OnCallstackEntry(CallstackEntryType eType, CallstackEntry &entry);
\r
123 virtual void OnDbgHelpErr(LPCSTR szFuncName, DWORD gle, DWORD64 addr);
\r
124 virtual void OnOutput(LPCSTR szText);
\r
126 stack_walkerInternal *m_sw;
\r
128 DWORD m_dwProcessId;
\r
129 BOOL m_modulesLoaded;
\r
134 static BOOL __stdcall myReadProcMem(HANDLE hProcess, DWORD64 qwBaseAddress, PVOID lpBuffer, DWORD nSize, LPDWORD lpNumberOfBytesRead);
\r
136 friend stack_walkerInternal;
\r
140 // The "ugly" assembler-implementation is needed for systems before XP
\r
141 // If you have a new PSDK and you only compile for XP and later, then you can use
\r
142 // the "RtlCaptureContext"
\r
143 // Currently there is no define which determines the PSDK-Version...
\r
144 // So we just use the compiler-version (and assumes that the PSDK is
\r
145 // the one which was installed by the VS-IDE)
\r
147 // INFO: If you want, you can use the RtlCaptureContext if you only target XP and later...
\r
148 // But I currently use it in x64/IA64 environments...
\r
149 //#if defined(_M_IX86) && (_WIN32_WINNT <= 0x0500) && (_MSC_VER < 1400)
\r
151 #if defined(_M_IX86)
\r
152 #ifdef CURRENT_THREAD_VIA_EXCEPTION
\r
153 // TODO: The following is not a "good" implementation,
\r
154 // because the callstack is only valid in the "__except" block...
\r
155 #define GET_CURRENT_CONTEXT(c, contextFlags) \
\r
157 memset(&c, 0, sizeof(CONTEXT)); \
\r
158 EXCEPTION_POINTERS *pExp = NULL; \
\r
161 } __except( ( (pExp = GetExceptionInformation()) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_EXECUTE_HANDLER)) {} \
\r
162 if (pExp != NULL) \
\r
163 memcpy(&c, pExp->ContextRecord, sizeof(CONTEXT)); \
\r
164 c.ContextFlags = contextFlags; \
\r
167 // The following should be enough for walking the callstack...
\r
168 #define GET_CURRENT_CONTEXT(c, contextFlags) \
\r
170 memset(&c, 0, sizeof(CONTEXT)); \
\r
171 c.ContextFlags = contextFlags; \
\r
174 __asm mov c.Eip, eax \
\r
175 __asm mov c.Ebp, ebp \
\r
176 __asm mov c.Esp, esp \
\r
182 // The following is defined for x86 (XP and higher), x64 and IA64:
\r
183 #define GET_CURRENT_CONTEXT(c, contextFlags) \
\r
185 memset(&c, 0, sizeof(CONTEXT)); \
\r
186 c.ContextFlags = contextFlags; \
\r
187 RtlCaptureContext(&c); \
\r