StackWalker.h 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. /**********************************************************************
  2. *
  3. * StackWalker.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 'StackWalker.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. #include <windows.h>
  15. #include <stdio.h>
  16. // special defines for VC5/6 (if no actual PSDK is installed):
  17. #if _MSC_VER < 1300
  18. typedef unsigned __int64 DWORD64, *PDWORD64;
  19. #if defined(_WIN64)
  20. typedef unsigned __int64 SIZE_T, *PSIZE_T;
  21. #else
  22. typedef unsigned long SIZE_T, *PSIZE_T;
  23. #endif
  24. #endif // _MSC_VER < 1300
  25. class StackWalkerInternal; // forward
  26. class StackWalker
  27. {
  28. public:
  29. typedef enum StackWalkOptions
  30. {
  31. // No addition info will be retrived
  32. // (only the address is available)
  33. RetrieveNone = 0,
  34. // Try to get the symbol-name
  35. RetrieveSymbol = 1,
  36. // Try to get the line for this symbol
  37. RetrieveLine = 2,
  38. // Try to retrieve the module-infos
  39. RetrieveModuleInfo = 4,
  40. // Also retrieve the version for the DLL/EXE
  41. RetrieveFileVersion = 8,
  42. // Contains all the abouve
  43. RetrieveVerbose = 0xF,
  44. // Generate a "good" symbol-search-path
  45. SymBuildPath = 0x10,
  46. // Also use the public Microsoft-Symbol-Server
  47. SymUseSymSrv = 0x20,
  48. // Contains all the abouve "Sym"-options
  49. SymAll = 0x30,
  50. // Contains all options (default)
  51. OptionsAll = 0x3F
  52. } StackWalkOptions;
  53. StackWalker(
  54. int fDescriptor,
  55. int options = OptionsAll, // 'int' is by design, to combine the enum-flags
  56. LPCSTR szSymPath = NULL,
  57. DWORD dwProcessId = GetCurrentProcessId(),
  58. HANDLE hProcess = GetCurrentProcess()
  59. );
  60. StackWalker(DWORD dwProcessId, HANDLE hProcess);
  61. virtual ~StackWalker();
  62. typedef BOOL (__stdcall *PReadProcessMemoryRoutine)(
  63. HANDLE hProcess,
  64. DWORD64 qwBaseAddress,
  65. PVOID lpBuffer,
  66. DWORD nSize,
  67. LPDWORD lpNumberOfBytesRead,
  68. LPVOID pUserData // optional data, which was passed in "ShowCallstack"
  69. );
  70. BOOL LoadModules();
  71. BOOL ShowCallstack(
  72. HANDLE hThread = GetCurrentThread(),
  73. const CONTEXT *context = NULL,
  74. PReadProcessMemoryRoutine readMemoryFunction = NULL,
  75. LPVOID pUserData = NULL // optional to identify some data in the 'readMemoryFunction'-callback
  76. );
  77. #if _MSC_VER >= 1300
  78. // due to some reasons, the "STACKWALK_MAX_NAMELEN" must be declared as "public"
  79. // in older compilers in order to use it... starting with VC7 we can declare it as "protected"
  80. protected:
  81. #endif
  82. enum { STACKWALK_MAX_NAMELEN = 1024 }; // max name length for found symbols
  83. protected:
  84. // Entry for each Callstack-Entry
  85. typedef struct CallstackEntry
  86. {
  87. DWORD64 offset; // if 0, we have no valid entry
  88. CHAR name[STACKWALK_MAX_NAMELEN];
  89. CHAR undName[STACKWALK_MAX_NAMELEN];
  90. CHAR undFullName[STACKWALK_MAX_NAMELEN];
  91. DWORD64 offsetFromSmybol;
  92. DWORD offsetFromLine;
  93. DWORD lineNumber;
  94. CHAR lineFileName[STACKWALK_MAX_NAMELEN];
  95. DWORD symType;
  96. LPCSTR symTypeString;
  97. CHAR moduleName[STACKWALK_MAX_NAMELEN];
  98. DWORD64 baseOfImage;
  99. CHAR loadedImageName[STACKWALK_MAX_NAMELEN];
  100. } CallstackEntry;
  101. typedef enum CallstackEntryType {firstEntry, nextEntry, lastEntry};
  102. virtual void OnSymInit(LPCSTR szSearchPath, DWORD symOptions, LPCSTR szUserName);
  103. virtual void OnLoadModule(LPCSTR img, LPCSTR mod, DWORD64 baseAddr, DWORD size, DWORD result, LPCSTR symType, LPCSTR pdbName, ULONGLONG fileVersion);
  104. virtual void OnCallstackEntry(CallstackEntryType eType, CallstackEntry &entry);
  105. virtual void OnOutput(LPCSTR szText);
  106. StackWalkerInternal *m_sw;
  107. HANDLE m_hProcess;
  108. DWORD m_dwProcessId;
  109. BOOL m_modulesLoaded;
  110. LPSTR m_szSymPath;
  111. int m_options;
  112. int m_fDescriptor;
  113. static BOOL __stdcall myReadProcMem(HANDLE hProcess, DWORD64 qwBaseAddress, PVOID lpBuffer, DWORD nSize, LPDWORD lpNumberOfBytesRead);
  114. friend StackWalkerInternal;
  115. };
  116. // The "ugly" assembler-implementation is needed for systems before XP
  117. // If you have a new PSDK and you only compile for XP and later, then you can use
  118. // the "RtlCaptureContext"
  119. // Currently there is no define which determines the PSDK-Version...
  120. // So we just use the compiler-version (and assumes that the PSDK is
  121. // the one which was installed by the VS-IDE)
  122. // INFO: If you want, you can use the RtlCaptureContext if you only target XP and later...
  123. // But I currently use it in x64/IA64 environments...
  124. //#if defined(_M_IX86) && (_WIN32_WINNT <= 0x0500) && (_MSC_VER < 1400)
  125. #if defined(_M_IX86)
  126. #ifdef CURRENT_THREAD_VIA_EXCEPTION
  127. // TODO: The following is not a "good" implementation,
  128. // because the callstack is only valid in the "__except" block...
  129. #define GET_CURRENT_CONTEXT(c, contextFlags) \
  130. do { \
  131. memset(&c, 0, sizeof(CONTEXT)); \
  132. EXCEPTION_POINTERS *pExp = NULL; \
  133. __try { \
  134. throw 0; \
  135. } __except( ( (pExp = GetExceptionInformation()) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_EXECUTE_HANDLER)) {} \
  136. if (pExp != NULL) \
  137. memcpy(&c, pExp->ContextRecord, sizeof(CONTEXT)); \
  138. c.ContextFlags = contextFlags; \
  139. } while(0);
  140. #else
  141. // The following should be enough for walking the callstack...
  142. #define GET_CURRENT_CONTEXT(c, contextFlags) \
  143. do { \
  144. memset(&c, 0, sizeof(CONTEXT)); \
  145. c.ContextFlags = contextFlags; \
  146. __asm call x \
  147. __asm x: pop eax \
  148. __asm mov c.Eip, eax \
  149. __asm mov c.Ebp, ebp \
  150. __asm mov c.Esp, esp \
  151. } while(0);
  152. #endif
  153. #else
  154. // The following is defined for x86 (XP and higher), x64 and IA64:
  155. #define GET_CURRENT_CONTEXT(c, contextFlags) \
  156. do { \
  157. memset(&c, 0, sizeof(CONTEXT)); \
  158. c.ContextFlags = contextFlags; \
  159. RtlCaptureContext(&c); \
  160. } while(0);
  161. #endif