StackWalker.h 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
  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. #include <windows.h>
  39. // special defines for VC5/6 (if no actual PSDK is installed):
  40. #if _MSC_VER < 1300
  41. typedef unsigned __int64 DWORD64, *PDWORD64;
  42. #if defined(_WIN64)
  43. typedef unsigned __int64 SIZE_T, *PSIZE_T;
  44. #else
  45. typedef unsigned long SIZE_T, *PSIZE_T;
  46. #endif
  47. #endif // _MSC_VER < 1300
  48. // If VC7 and later, then use the shipped 'dbghelp.h'-file
  49. #pragma pack(push,8)
  50. #if _MSC_VER >= 1300
  51. #include <dbghelp.h>
  52. #else
  53. // inline the important dbghelp.h-declarations...
  54. typedef enum {
  55. SymNone = 0,
  56. SymCoff,
  57. SymCv,
  58. SymPdb,
  59. SymExport,
  60. SymDeferred,
  61. SymSym,
  62. SymDia,
  63. SymVirtual,
  64. NumSymTypes
  65. } SYM_TYPE;
  66. typedef struct _IMAGEHLP_LINE64 {
  67. DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_LINE64)
  68. PVOID Key; // internal
  69. DWORD LineNumber; // line number in file
  70. PCHAR FileName; // full filename
  71. DWORD64 Address; // first instruction of line
  72. } IMAGEHLP_LINE64, *PIMAGEHLP_LINE64;
  73. typedef struct _IMAGEHLP_MODULE64 {
  74. DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_MODULE64)
  75. DWORD64 BaseOfImage; // base load address of module
  76. DWORD ImageSize; // virtual size of the loaded module
  77. DWORD TimeDateStamp; // date/time stamp from pe header
  78. DWORD CheckSum; // checksum from the pe header
  79. DWORD NumSyms; // number of symbols in the symbol table
  80. SYM_TYPE SymType; // type of symbols loaded
  81. CHAR ModuleName[32]; // module name
  82. CHAR ImageName[256]; // image name
  83. CHAR LoadedImageName[256]; // symbol file name
  84. } IMAGEHLP_MODULE64, *PIMAGEHLP_MODULE64;
  85. typedef struct _IMAGEHLP_SYMBOL64 {
  86. DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_SYMBOL64)
  87. DWORD64 Address; // virtual address including dll base address
  88. DWORD Size; // estimated size of symbol, can be zero
  89. DWORD Flags; // info about the symbols, see the SYMF defines
  90. DWORD MaxNameLength; // maximum size of symbol name in 'Name'
  91. CHAR Name[1]; // symbol name (null terminated string)
  92. } IMAGEHLP_SYMBOL64, *PIMAGEHLP_SYMBOL64;
  93. typedef enum {
  94. AddrMode1616,
  95. AddrMode1632,
  96. AddrModeReal,
  97. AddrModeFlat
  98. } ADDRESS_MODE;
  99. typedef struct _tagADDRESS64 {
  100. DWORD64 Offset;
  101. WORD Segment;
  102. ADDRESS_MODE Mode;
  103. } ADDRESS64, *LPADDRESS64;
  104. typedef struct _KDHELP64 {
  105. DWORD64 Thread;
  106. DWORD ThCallbackStack;
  107. DWORD ThCallbackBStore;
  108. DWORD NextCallback;
  109. DWORD FramePointer;
  110. DWORD64 KiCallUserMode;
  111. DWORD64 KeUserCallbackDispatcher;
  112. DWORD64 SystemRangeStart;
  113. DWORD64 Reserved[8];
  114. } KDHELP64, *PKDHELP64;
  115. typedef struct _tagSTACKFRAME64 {
  116. ADDRESS64 AddrPC; // program counter
  117. ADDRESS64 AddrReturn; // return address
  118. ADDRESS64 AddrFrame; // frame pointer
  119. ADDRESS64 AddrStack; // stack pointer
  120. ADDRESS64 AddrBStore; // backing store pointer
  121. PVOID FuncTableEntry; // pointer to pdata/fpo or NULL
  122. DWORD64 Params[4]; // possible arguments to the function
  123. BOOL Far; // WOW far call
  124. BOOL Virtual; // is this a virtual frame?
  125. DWORD64 Reserved[3];
  126. KDHELP64 KdHelp;
  127. } STACKFRAME64, *LPSTACKFRAME64;
  128. typedef
  129. BOOL
  130. (__stdcall *PREAD_PROCESS_MEMORY_ROUTINE64)(
  131. HANDLE hProcess,
  132. DWORD64 qwBaseAddress,
  133. PVOID lpBuffer,
  134. DWORD nSize,
  135. LPDWORD lpNumberOfBytesRead
  136. );
  137. typedef
  138. PVOID
  139. (__stdcall *PFUNCTION_TABLE_ACCESS_ROUTINE64)(
  140. HANDLE hProcess,
  141. DWORD64 AddrBase
  142. );
  143. typedef
  144. DWORD64
  145. (__stdcall *PGET_MODULE_BASE_ROUTINE64)(
  146. HANDLE hProcess,
  147. DWORD64 Address
  148. );
  149. typedef
  150. DWORD64
  151. (__stdcall *PTRANSLATE_ADDRESS_ROUTINE64)(
  152. HANDLE hProcess,
  153. HANDLE hThread,
  154. LPADDRESS64 lpaddr
  155. );
  156. #define SYMOPT_CASE_INSENSITIVE 0x00000001
  157. #define SYMOPT_UNDNAME 0x00000002
  158. #define SYMOPT_DEFERRED_LOADS 0x00000004
  159. #define SYMOPT_NO_CPP 0x00000008
  160. #define SYMOPT_LOAD_LINES 0x00000010
  161. #define SYMOPT_OMAP_FIND_NEAREST 0x00000020
  162. #define SYMOPT_LOAD_ANYTHING 0x00000040
  163. #define SYMOPT_IGNORE_CVREC 0x00000080
  164. #define SYMOPT_NO_UNQUALIFIED_LOADS 0x00000100
  165. #define SYMOPT_FAIL_CRITICAL_ERRORS 0x00000200
  166. #define SYMOPT_EXACT_SYMBOLS 0x00000400
  167. #define SYMOPT_ALLOW_ABSOLUTE_SYMBOLS 0x00000800
  168. #define SYMOPT_IGNORE_NT_SYMPATH 0x00001000
  169. #define SYMOPT_INCLUDE_32BIT_MODULES 0x00002000
  170. #define SYMOPT_PUBLICS_ONLY 0x00004000
  171. #define SYMOPT_NO_PUBLICS 0x00008000
  172. #define SYMOPT_AUTO_PUBLICS 0x00010000
  173. #define SYMOPT_NO_IMAGE_SEARCH 0x00020000
  174. #define SYMOPT_SECURE 0x00040000
  175. #define SYMOPT_DEBUG 0x80000000
  176. #define UNDNAME_COMPLETE (0x0000) // Enable full undecoration
  177. #define UNDNAME_NAME_ONLY (0x1000) // Crack only the name for primary declaration;
  178. #endif // _MSC_VER < 1300
  179. #pragma pack(pop)
  180. class StackWalkerInternal; // forward
  181. class StackWalker
  182. {
  183. public:
  184. bool fast;
  185. typedef enum StackWalkOptions
  186. {
  187. // No addition info will be retrived
  188. // (only the address is available)
  189. RetrieveNone = 0,
  190. // Try to get the symbol-name
  191. RetrieveSymbol = 1,
  192. // Try to get the line for this symbol
  193. RetrieveLine = 2,
  194. // Try to retrieve the module-infos
  195. RetrieveModuleInfo = 4,
  196. // Also retrieve the version for the DLL/EXE
  197. RetrieveFileVersion = 8,
  198. // Contains all the abouve
  199. RetrieveVerbose = 0xF,
  200. // Generate a "good" symbol-search-path
  201. SymBuildPath = 0x10,
  202. // Also use the public Microsoft-Symbol-Server
  203. SymUseSymSrv = 0x20,
  204. // Contains all the abouve "Sym"-options
  205. SymAll = 0x30,
  206. // Contains all options (default)
  207. OptionsAll = 0x3F
  208. } StackWalkOptions;
  209. StackWalker(
  210. int options = OptionsAll, // 'int' is by design, to combine the enum-flags
  211. LPCSTR szSymPath = NULL,
  212. DWORD dwProcessId = GetCurrentProcessId(),
  213. HANDLE hProcess = GetCurrentProcess()
  214. );
  215. StackWalker(DWORD dwProcessId, HANDLE hProcess);
  216. virtual ~StackWalker();
  217. typedef BOOL (__stdcall *PReadProcessMemoryRoutine)(
  218. HANDLE hProcess,
  219. DWORD64 qwBaseAddress,
  220. PVOID lpBuffer,
  221. DWORD nSize,
  222. LPDWORD lpNumberOfBytesRead,
  223. LPVOID pUserData // optional data, which was passed in "ShowCallstack"
  224. );
  225. BOOL LoadModules();
  226. BOOL ShowCallstack(
  227. HANDLE hThread = GetCurrentThread(),
  228. const CONTEXT *context = NULL,
  229. PReadProcessMemoryRoutine readMemoryFunction = NULL,
  230. LPVOID pUserData = NULL // optional to identify some data in the 'readMemoryFunction'-callback
  231. );
  232. #if _MSC_VER >= 1300
  233. // due to some reasons, the "STACKWALK_MAX_NAMELEN" must be declared as "public"
  234. // in older compilers in order to use it... starting with VC7 we can declare it as "protected"
  235. protected:
  236. #endif
  237. enum { STACKWALK_MAX_NAMELEN = 512 }; // max name length for found symbols
  238. protected:
  239. // Entry for each Callstack-Entry
  240. typedef struct CallstackEntry
  241. {
  242. DWORD64 offset; // if 0, we have no valid entry
  243. CHAR name[STACKWALK_MAX_NAMELEN];
  244. CHAR undName[STACKWALK_MAX_NAMELEN];
  245. CHAR undFullName[STACKWALK_MAX_NAMELEN];
  246. DWORD64 offsetFromSmybol;
  247. DWORD offsetFromLine;
  248. DWORD lineNumber;
  249. CHAR lineFileName[STACKWALK_MAX_NAMELEN];
  250. DWORD symType;
  251. LPCSTR symTypeString;
  252. CHAR moduleName[STACKWALK_MAX_NAMELEN];
  253. DWORD64 baseOfImage;
  254. CHAR loadedImageName[STACKWALK_MAX_NAMELEN];
  255. } CallstackEntry;
  256. enum CallstackEntryType {firstEntry, nextEntry, lastEntry};
  257. virtual void OnSymInit(LPCSTR szSearchPath, DWORD symOptions, LPCSTR szUserName);
  258. virtual void OnLoadModule(LPCSTR img, LPCSTR mod, DWORD64 baseAddr, DWORD size, DWORD result, LPCSTR symType, LPCSTR pdbName, ULONGLONG fileVersion);
  259. virtual void OnCallstackEntry(CallstackEntryType eType, CallstackEntry &entry);
  260. virtual void OnDbgHelpErr(LPCSTR szFuncName, DWORD gle, DWORD64 addr);
  261. virtual void OnOutput(LPCSTR szText);
  262. StackWalkerInternal *m_sw;
  263. HANDLE m_hProcess;
  264. DWORD m_dwProcessId;
  265. BOOL m_modulesLoaded;
  266. LPSTR m_szSymPath;
  267. Byte pSym_temp[SIZEI(IMAGEHLP_SYMBOL64) + STACKWALK_MAX_NAMELEN];
  268. int m_options;
  269. int m_MaxRecursionCount;
  270. static BOOL __stdcall myReadProcMem(HANDLE hProcess, DWORD64 qwBaseAddress, PVOID lpBuffer, DWORD nSize, LPDWORD lpNumberOfBytesRead);
  271. friend StackWalkerInternal;
  272. }; // class StackWalker
  273. // The "ugly" assembler-implementation is needed for systems before XP
  274. // If you have a new PSDK and you only compile for XP and later, then you can use
  275. // the "RtlCaptureContext"
  276. // Currently there is no define which determines the PSDK-Version...
  277. // So we just use the compiler-version (and assumes that the PSDK is
  278. // the one which was installed by the VS-IDE)
  279. // INFO: If you want, you can use the RtlCaptureContext if you only target XP and later...
  280. // But I currently use it in x64/IA64 environments...
  281. //#if defined(_M_IX86) && (_WIN32_WINNT <= 0x0500) && (_MSC_VER < 1400)
  282. #if defined(_M_IX86)
  283. #ifdef CURRENT_THREAD_VIA_EXCEPTION
  284. // TODO: The following is not a "good" implementation,
  285. // because the callstack is only valid in the "__except" block...
  286. #define GET_CURRENT_CONTEXT(c, contextFlags) \
  287. do { \
  288. memset(&c, 0, sizeof(CONTEXT)); \
  289. EXCEPTION_POINTERS *pExp = NULL; \
  290. __try { \
  291. throw 0; \
  292. } __except( ( (pExp = GetExceptionInformation()) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_EXECUTE_HANDLER)) {} \
  293. if (pExp != NULL) \
  294. memcpy(&c, pExp->ContextRecord, sizeof(CONTEXT)); \
  295. c.ContextFlags = contextFlags; \
  296. } while(0);
  297. #else
  298. // The following should be enough for walking the callstack...
  299. #define GET_CURRENT_CONTEXT(c, contextFlags) \
  300. do { \
  301. memset(&c, 0, sizeof(CONTEXT)); \
  302. c.ContextFlags = contextFlags; \
  303. __asm call x \
  304. __asm x: pop eax \
  305. __asm mov c.Eip, eax \
  306. __asm mov c.Ebp, ebp \
  307. __asm mov c.Esp, esp \
  308. } while(0);
  309. #endif
  310. #else
  311. // The following is defined for x86 (XP and higher), x64 and IA64:
  312. #define GET_CURRENT_CONTEXT(c, contextFlags) \
  313. do { \
  314. memset(&c, 0, sizeof(CONTEXT)); \
  315. c.ContextFlags = contextFlags; \
  316. RtlCaptureContext(&c); \
  317. } while(0);
  318. #endif