]> git.sesse.net Git - casparcg/blob - common/compiler/vs/stack_walker.h
set svn:eol-style native on .h and .cpp files
[casparcg] / common / compiler / vs / stack_walker.h
1 /**********************************************************************
2  * 
3  * stack_walker.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 'stack_walker.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 stack_walkerInternal;  // forward
28 class stack_walker
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   stack_walker(
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   stack_walker(DWORD dwProcessId, HANDLE hProcess);
72   virtual ~stack_walker();
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   stack_walkerInternal *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 stack_walkerInternal;
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