1 /**********************************************************************
7 * 2005-07-27 v1 - First public release on http://www.codeproject.com/
8 * (for additional changes see History in 'stack_walker.cpp'!
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)!
17 // special defines for VC5/6 (if no actual PSDK is installed):
19 typedef unsigned __int64 DWORD64, *PDWORD64;
21 typedef unsigned __int64 SIZE_T, *PSIZE_T;
23 typedef unsigned long SIZE_T, *PSIZE_T;
25 #endif // _MSC_VER < 1300
27 class stack_walkerInternal; // forward
31 typedef enum StackWalkOptions
33 // No addition info will be retrived
34 // (only the address is available)
37 // Try to get the symbol-name
40 // Try to get the line for this symbol
43 // Try to retrieve the module-infos
44 RetrieveModuleInfo = 4,
46 // Also retrieve the version for the DLL/EXE
47 RetrieveFileVersion = 8,
49 // Contains all the abouve
50 RetrieveVerbose = 0xF,
52 // Generate a "good" symbol-search-path
55 // Also use the public Microsoft-Symbol-Server
58 // Contains all the abouve "Sym"-options
61 // Contains all options (default)
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()
71 stack_walker(DWORD dwProcessId, HANDLE hProcess);
72 virtual ~stack_walker();
74 typedef BOOL (__stdcall *PReadProcessMemoryRoutine)(
76 DWORD64 qwBaseAddress,
79 LPDWORD lpNumberOfBytesRead,
80 LPVOID pUserData // optional data, which was passed in "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
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"
97 enum { STACKWALK_MAX_NAMELEN = 1024 }; // max name length for found symbols
100 // Entry for each Callstack-Entry
101 typedef struct CallstackEntry
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;
110 CHAR lineFileName[STACKWALK_MAX_NAMELEN];
112 LPCSTR symTypeString;
113 CHAR moduleName[STACKWALK_MAX_NAMELEN];
115 CHAR loadedImageName[STACKWALK_MAX_NAMELEN];
118 typedef enum CallstackEntryType {firstEntry, nextEntry, lastEntry};
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);
126 stack_walkerInternal *m_sw;
129 BOOL m_modulesLoaded;
134 static BOOL __stdcall myReadProcMem(HANDLE hProcess, DWORD64 qwBaseAddress, PVOID lpBuffer, DWORD nSize, LPDWORD lpNumberOfBytesRead);
136 friend stack_walkerInternal;
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)
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)
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) \
157 memset(&c, 0, sizeof(CONTEXT)); \
158 EXCEPTION_POINTERS *pExp = NULL; \
161 } __except( ( (pExp = GetExceptionInformation()) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_EXECUTE_HANDLER)) {} \
163 memcpy(&c, pExp->ContextRecord, sizeof(CONTEXT)); \
164 c.ContextFlags = contextFlags; \
167 // The following should be enough for walking the callstack...
168 #define GET_CURRENT_CONTEXT(c, contextFlags) \
170 memset(&c, 0, sizeof(CONTEXT)); \
171 c.ContextFlags = contextFlags; \
174 __asm mov c.Eip, eax \
175 __asm mov c.Ebp, ebp \
176 __asm mov c.Esp, esp \
182 // The following is defined for x86 (XP and higher), x64 and IA64:
183 #define GET_CURRENT_CONTEXT(c, contextFlags) \
185 memset(&c, 0, sizeof(CONTEXT)); \
186 c.ContextFlags = contextFlags; \
187 RtlCaptureContext(&c); \