Developer Documentation
Loading...
Searching...
No Matches
StackWalker.hh
1/**********************************************************************
2 *
3 * StackWalker.h
4 *
5 *
6 *
7 * LICENSE (http://www.opensource.org/licenses/bsd-license.php)
8 *
9 * Copyright (c) 2005-2009, Jochen Kalmbach
10 * All rights reserved.
11 *
12 * Redistribution and use in source and binary forms, with or without modification,
13 * are permitted provided that the following conditions are met:
14 *
15 * Redistributions of source code must retain the above copyright notice,
16 * this list of conditions and the following disclaimer.
17 * Redistributions in binary form must reproduce the above copyright notice,
18 * this list of conditions and the following disclaimer in the documentation
19 * and/or other materials provided with the distribution.
20 * Neither the name of Jochen Kalmbach nor the names of its contributors may be
21 * used to endorse or promote products derived from this software without
22 * specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
24 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
25 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
28 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
30 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
32 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 *
34 * **********************************************************************/
35// #pragma once is supported starting with _MCS_VER 1000,
36// so we need not to check the version (because we only support _MSC_VER >= 1100)!
37#pragma once
38
39#include <windows.h>
40
41#if _MSC_VER >= 1900
42#pragma warning(disable : 4091)
43#endif
44
45// special defines for VC5/6 (if no actual PSDK is installed):
46#if _MSC_VER < 1300
47typedef unsigned __int64 DWORD64, *PDWORD64;
48#if defined(_WIN64)
49typedef unsigned __int64 SIZE_T, *PSIZE_T;
50#else
51typedef unsigned long SIZE_T, *PSIZE_T;
52#endif
53#endif // _MSC_VER < 1300
54
55class StackWalkerInternal; // forward
57{
58public:
59 typedef enum StackWalkOptions
60 {
61 // No addition info will be retrived
62 // (only the address is available)
63 RetrieveNone = 0,
64
65 // Try to get the symbol-name
66 RetrieveSymbol = 1,
67
68 // Try to get the line for this symbol
69 RetrieveLine = 2,
70
71 // Try to retrieve the module-infos
72 RetrieveModuleInfo = 4,
73
74 // Also retrieve the version for the DLL/EXE
75 RetrieveFileVersion = 8,
76
77 // Contains all the abouve
78 RetrieveVerbose = 0xF,
79
80 // Generate a "good" symbol-search-path
81 SymBuildPath = 0x10,
82
83 // Also use the public Microsoft-Symbol-Server
84 SymUseSymSrv = 0x20,
85
86 // Contains all the abouve "Sym"-options
87 SymAll = 0x30,
88
89 // Contains all options (default)
90 OptionsAll = 0x3F
91 } StackWalkOptions;
92
94 int options = OptionsAll, // 'int' is by design, to combine the enum-flags
95 LPCSTR szSymPath = NULL,
96 DWORD dwProcessId = GetCurrentProcessId(),
97 HANDLE hProcess = GetCurrentProcess()
98 );
99 StackWalker(DWORD dwProcessId, HANDLE hProcess);
100 virtual ~StackWalker();
101
102 typedef BOOL (__stdcall *PReadProcessMemoryRoutine)(
103 HANDLE hProcess,
104 DWORD64 qwBaseAddress,
105 PVOID lpBuffer,
106 DWORD nSize,
107 LPDWORD lpNumberOfBytesRead,
108 LPVOID pUserData // optional data, which was passed in "ShowCallstack"
109 );
110
111 BOOL LoadModules();
112
113 BOOL ShowCallstack(
114 HANDLE hThread = GetCurrentThread(),
115 const CONTEXT *context = NULL,
116 PReadProcessMemoryRoutine readMemoryFunction = NULL,
117 LPVOID pUserData = NULL // optional to identify some data in the 'readMemoryFunction'-callback
118 );
119
120#if _MSC_VER >= 1300
121// due to some reasons, the "STACKWALK_MAX_NAMELEN" must be declared as "public"
122// in older compilers in order to use it... starting with VC7 we can declare it as "protected"
123protected:
124#endif
125 enum { STACKWALK_MAX_NAMELEN = 1024 }; // max name length for found symbols
126
127protected:
128 // Entry for each Callstack-Entry
129 typedef struct CallstackEntry
130 {
131 DWORD64 offset; // if 0, we have no valid entry
132 CHAR name[STACKWALK_MAX_NAMELEN];
133 CHAR undName[STACKWALK_MAX_NAMELEN];
134 CHAR undFullName[STACKWALK_MAX_NAMELEN];
135 DWORD64 offsetFromSmybol;
136 DWORD offsetFromLine;
137 DWORD lineNumber;
138 CHAR lineFileName[STACKWALK_MAX_NAMELEN];
139 DWORD symType;
140 LPCSTR symTypeString;
141 CHAR moduleName[STACKWALK_MAX_NAMELEN];
142 DWORD64 baseOfImage;
143 CHAR loadedImageName[STACKWALK_MAX_NAMELEN];
145
146 typedef enum CallstackEntryType {firstEntry, nextEntry, lastEntry};
147
148 virtual void OnSymInit(LPCSTR szSearchPath, DWORD symOptions, LPCSTR szUserName);
149 virtual void OnLoadModule(LPCSTR img, LPCSTR mod, DWORD64 baseAddr, DWORD size, DWORD result, LPCSTR symType, LPCSTR pdbName, ULONGLONG fileVersion);
150 virtual void OnCallstackEntry(CallstackEntryType eType, CallstackEntry &entry);
151 virtual void OnDbgHelpErr(LPCSTR szFuncName, DWORD gle, DWORD64 addr);
152 virtual void OnOutput(LPCSTR szText);
153
155 HANDLE m_hProcess;
156 DWORD m_dwProcessId;
157 BOOL m_modulesLoaded;
158 LPSTR m_szSymPath;
159
160 int m_options;
161 int m_MaxRecursionCount;
162
163 static BOOL __stdcall myReadProcMem(HANDLE hProcess, DWORD64 qwBaseAddress, PVOID lpBuffer, DWORD nSize, LPDWORD lpNumberOfBytesRead);
164
165 friend StackWalkerInternal;
166}; // class StackWalker
167
168
169// The "ugly" assembler-implementation is needed for systems before XP
170// If you have a new PSDK and you only compile for XP and later, then you can use
171// the "RtlCaptureContext"
172// Currently there is no define which determines the PSDK-Version...
173// So we just use the compiler-version (and assumes that the PSDK is
174// the one which was installed by the VS-IDE)
175
176// INFO: If you want, you can use the RtlCaptureContext if you only target XP and later...
177// But I currently use it in x64/IA64 environments...
178//#if defined(_M_IX86) && (_WIN32_WINNT <= 0x0500) && (_MSC_VER < 1400)
179
180#if defined(_M_IX86)
181#ifdef CURRENT_THREAD_VIA_EXCEPTION
182// TODO: The following is not a "good" implementation,
183// because the callstack is only valid in the "__except" block...
184#define GET_CURRENT_CONTEXT_STACKWALKER_CODEPLEX(c, contextFlags) \
185 do { \
186 memset(&c, 0, sizeof(CONTEXT)); \
187 EXCEPTION_POINTERS *pExp = NULL; \
188 __try { \
189 throw 0; \
190 } __except( ( (pExp = GetExceptionInformation()) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_EXECUTE_HANDLER)) {} \
191 if (pExp != NULL) \
192 memcpy(&c, pExp->ContextRecord, sizeof(CONTEXT)); \
193 c.ContextFlags = contextFlags; \
194 } while(0);
195#else
196// The following should be enough for walking the callstack...
197#define GET_CURRENT_CONTEXT_STACKWALKER_CODEPLEX(c, contextFlags) \
198 do { \
199 memset(&c, 0, sizeof(CONTEXT)); \
200 c.ContextFlags = contextFlags; \
201 __asm call x \
202 __asm x: pop eax \
203 __asm mov c.Eip, eax \
204 __asm mov c.Ebp, ebp \
205 __asm mov c.Esp, esp \
206 } while(0);
207#endif
208
209#else
210
211// The following is defined for x86 (XP and higher), x64 and IA64:
212#define GET_CURRENT_CONTEXT_STACKWALKER_CODEPLEX(c, contextFlags) \
213 do { \
214 memset(&c, 0, sizeof(CONTEXT)); \
215 c.ContextFlags = contextFlags; \
216 RtlCaptureContext(&c); \
217} while(0);
218#endif