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