]> git.sesse.net Git - casparcg/blob - common/compiler/vs/stack_walker.h
0eb3384c79691c20c866b72145a2bc6e9b3e94dc
[casparcg] / common / compiler / vs / stack_walker.h
1 /**********************************************************************\r
2  * \r
3  * stack_walker.h\r
4  *\r
5  *\r
6  * History:\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
9  *\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
13 #pragma once\r
14 \r
15 #include <windows.h>\r
16 \r
17 // special defines for VC5/6 (if no actual PSDK is installed):\r
18 #if _MSC_VER < 1300\r
19 typedef unsigned __int64 DWORD64, *PDWORD64;\r
20 #if defined(_WIN64)\r
21 typedef unsigned __int64 SIZE_T, *PSIZE_T;\r
22 #else\r
23 typedef unsigned long SIZE_T, *PSIZE_T;\r
24 #endif\r
25 #endif  // _MSC_VER < 1300\r
26 \r
27 class stack_walkerInternal;  // forward\r
28 class stack_walker\r
29 {\r
30 public:\r
31   typedef enum StackWalkOptions\r
32   {\r
33     // No addition info will be retrived \r
34     // (only the address is available)\r
35     RetrieveNone = 0,\r
36     \r
37     // Try to get the symbol-name\r
38     RetrieveSymbol = 1,\r
39     \r
40     // Try to get the line for this symbol\r
41     RetrieveLine = 2,\r
42     \r
43     // Try to retrieve the module-infos\r
44     RetrieveModuleInfo = 4,\r
45     \r
46     // Also retrieve the version for the DLL/EXE\r
47     RetrieveFileVersion = 8,\r
48     \r
49     // Contains all the abouve\r
50     RetrieveVerbose = 0xF,\r
51     \r
52     // Generate a "good" symbol-search-path\r
53     SymBuildPath = 0x10,\r
54     \r
55     // Also use the public Microsoft-Symbol-Server\r
56     SymUseSymSrv = 0x20,\r
57     \r
58     // Contains all the abouve "Sym"-options\r
59     SymAll = 0x30,\r
60     \r
61     // Contains all options (default)\r
62     OptionsAll = 0x3F\r
63   } StackWalkOptions;\r
64 \r
65   stack_walker(\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
70     );\r
71   stack_walker(DWORD dwProcessId, HANDLE hProcess);\r
72   virtual ~stack_walker();\r
73 \r
74   typedef BOOL (__stdcall *PReadProcessMemoryRoutine)(\r
75     HANDLE      hProcess,\r
76     DWORD64     qwBaseAddress,\r
77     PVOID       lpBuffer,\r
78     DWORD       nSize,\r
79     LPDWORD     lpNumberOfBytesRead,\r
80     LPVOID      pUserData  // optional data, which was passed in "ShowCallstack"\r
81     );\r
82 \r
83   BOOL LoadModules();\r
84 \r
85   BOOL 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
90     );\r
91 \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
95 protected:\r
96 #endif\r
97         enum { STACKWALK_MAX_NAMELEN = 1024 }; // max name length for found symbols\r
98 \r
99 protected:\r
100   // Entry for each Callstack-Entry\r
101   typedef struct CallstackEntry\r
102   {\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
109     DWORD lineNumber;\r
110     CHAR lineFileName[STACKWALK_MAX_NAMELEN];\r
111     DWORD symType;\r
112     LPCSTR symTypeString;\r
113     CHAR moduleName[STACKWALK_MAX_NAMELEN];\r
114     DWORD64 baseOfImage;\r
115     CHAR loadedImageName[STACKWALK_MAX_NAMELEN];\r
116   } CallstackEntry;\r
117 \r
118   typedef enum CallstackEntryType {firstEntry, nextEntry, lastEntry};\r
119 \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
125 \r
126   stack_walkerInternal *m_sw;\r
127   HANDLE m_hProcess;\r
128   DWORD m_dwProcessId;\r
129   BOOL m_modulesLoaded;\r
130   LPSTR m_szSymPath;\r
131 \r
132   int m_options;\r
133 \r
134   static BOOL __stdcall myReadProcMem(HANDLE hProcess, DWORD64 qwBaseAddress, PVOID lpBuffer, DWORD nSize, LPDWORD lpNumberOfBytesRead);\r
135 \r
136   friend stack_walkerInternal;\r
137 };\r
138 \r
139 \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
146 \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
150 \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
156   do { \\r
157     memset(&c, 0, sizeof(CONTEXT)); \\r
158     EXCEPTION_POINTERS *pExp = NULL; \\r
159     __try { \\r
160       throw 0; \\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
165   } while(0);\r
166 #else\r
167 // The following should be enough for walking the callstack...\r
168 #define GET_CURRENT_CONTEXT(c, contextFlags) \\r
169   do { \\r
170     memset(&c, 0, sizeof(CONTEXT)); \\r
171     c.ContextFlags = contextFlags; \\r
172     __asm    call x \\r
173     __asm x: pop eax \\r
174     __asm    mov c.Eip, eax \\r
175     __asm    mov c.Ebp, ebp \\r
176     __asm    mov c.Esp, esp \\r
177   } while(0);\r
178 #endif\r
179 \r
180 #else\r
181 \r
182 // The following is defined for x86 (XP and higher), x64 and IA64:\r
183 #define GET_CURRENT_CONTEXT(c, contextFlags) \\r
184   do { \\r
185     memset(&c, 0, sizeof(CONTEXT)); \\r
186     c.ContextFlags = contextFlags; \\r
187     RtlCaptureContext(&c); \\r
188 } while(0);\r
189 #endif\r