eathread_callstack_win64.cpp 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622
  1. ///////////////////////////////////////////////////////////////////////////////
  2. // Copyright (c) Electronic Arts Inc. All rights reserved.
  3. ///////////////////////////////////////////////////////////////////////////////
  4. #include <eathread/eathread_callstack.h>
  5. #include <eathread/eathread_callstack_context.h>
  6. #include <stdio.h>
  7. #include <string.h>
  8. #include <eathread/eathread_storage.h>
  9. #if defined(_WIN32_WINNT) && (_WIN32_WINNT < 0x0500)
  10. #undef _WIN32_WINNT
  11. #define _WIN32_WINNT 0x0500
  12. #endif
  13. #ifdef EA_COMPILER_MSVC
  14. EA_DISABLE_ALL_VC_WARNINGS()
  15. #include <Windows.h>
  16. #include <math.h> // VS2008 has an acknowledged bug that requires math.h (and possibly also string.h) to be #included before intrin.h.
  17. #include <intrin.h>
  18. #pragma intrinsic(_ReturnAddress)
  19. #if EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP)
  20. #include <winternl.h>
  21. #else
  22. // Temporary while waiting for formal support:
  23. extern "C" NTSYSAPI PEXCEPTION_ROUTINE NTAPI RtlVirtualUnwind(DWORD, DWORD64, DWORD64, PRUNTIME_FUNCTION, PCONTEXT, PVOID*, PDWORD64, PKNONVOLATILE_CONTEXT_POINTERS);
  24. extern "C" WINBASEAPI DWORD WINAPI GetModuleFileNameA(HMODULE, LPSTR, DWORD);
  25. #endif
  26. EA_RESTORE_ALL_VC_WARNINGS()
  27. #else
  28. #include <Windows.h>
  29. #include <winternl.h>
  30. #endif
  31. // Disable optimization of this code under VC++ for x64.
  32. // This is due to some as-yet undetermined crash that happens
  33. // when compiler optimizations are enabled for this code.
  34. // This function is not performance-sensitive and so disabling
  35. // optimizations shouldn't matter.
  36. #if defined(EA_COMPILER_MSVC) && defined(EA_PLATFORM_WIN64)
  37. #pragma optimize("", off)
  38. #endif
  39. ///////////////////////////////////////////////////////////////////////////////
  40. // Stuff that is supposed to be in windows.h and/or winternl.h but isn't
  41. // consistently present in all versions.
  42. //
  43. #ifndef UNW_FLAG_NHANDLER
  44. #define UNW_FLAG_NHANDLER 0
  45. #endif
  46. #ifndef UNWIND_HISTORY_TABLE_SIZE
  47. extern "C"
  48. {
  49. #define UNWIND_HISTORY_TABLE_SIZE 12
  50. #define UNWIND_HISTORY_TABLE_NONE 0
  51. #define UNWIND_HISTORY_TABLE_GLOBAL 1
  52. #define UNWIND_HISTORY_TABLE_LOCAL 2
  53. typedef struct _UNWIND_HISTORY_TABLE_ENTRY
  54. {
  55. ULONG64 ImageBase;
  56. PRUNTIME_FUNCTION FunctionEntry;
  57. } UNWIND_HISTORY_TABLE_ENTRY, *PUNWIND_HISTORY_TABLE_ENTRY;
  58. typedef struct _UNWIND_HISTORY_TABLE
  59. {
  60. ULONG Count;
  61. UCHAR Search;
  62. ULONG64 LowAddress;
  63. ULONG64 HighAddress;
  64. UNWIND_HISTORY_TABLE_ENTRY Entry[UNWIND_HISTORY_TABLE_SIZE];
  65. } UNWIND_HISTORY_TABLE, *PUNWIND_HISTORY_TABLE;
  66. PVOID WINAPI RtlLookupFunctionEntry(ULONG64 ControlPC, PULONG64 ImageBase, PUNWIND_HISTORY_TABLE HistoryTable);
  67. #if !defined(EA_COMPILER_MSVC) || (EA_COMPILER_MSVC < 1500) // if earlier than VS2008...
  68. typedef struct _KNONVOLATILE_CONTEXT_POINTERS
  69. {
  70. PULONGLONG dummy;
  71. } KNONVOLATILE_CONTEXT_POINTERS, *PKNONVOLATILE_CONTEXT_POINTERS;
  72. typedef struct _FRAME_POINTERS
  73. {
  74. ULONGLONG MemoryStackFp;
  75. ULONGLONG BackingStoreFp;
  76. } FRAME_POINTERS, *PFRAME_POINTERS;
  77. ULONGLONG WINAPI RtlVirtualUnwind(ULONG HandlerType, ULONGLONG ImageBase, ULONGLONG ControlPC,
  78. PRUNTIME_FUNCTION FunctionEntry, PCONTEXT ContextRecord, PBOOLEAN InFunction,
  79. PFRAME_POINTERS EstablisherFrame, PKNONVOLATILE_CONTEXT_POINTERS ContextPointers);
  80. #endif
  81. }
  82. #endif
  83. extern "C" WINBASEAPI DWORD WINAPI GetThreadId(_In_ HANDLE hThread);
  84. ///////////////////////////////////////////////////////////////////////////////
  85. namespace EA
  86. {
  87. namespace Thread
  88. {
  89. /* To consider: Enable usage of this below.
  90. ///////////////////////////////////////////////////////////////////////////////
  91. // IsAddressReadable
  92. //
  93. static bool IsAddressReadable(const void* pAddress)
  94. {
  95. bool bPageReadable;
  96. MEMORY_BASIC_INFORMATION mbi;
  97. if(VirtualQuery(pAddress, &mbi, sizeof(mbi)))
  98. {
  99. const DWORD flags = (PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_READONLY | PAGE_READWRITE);
  100. bPageReadable = (mbi.State == MEM_COMMIT) && ((mbi.Protect & flags) != 0);
  101. }
  102. else
  103. bPageReadable = false;
  104. return bPageReadable;
  105. }
  106. */
  107. #if !EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP)
  108. // GetRSP
  109. //
  110. // Returns the RSP of the caller.
  111. //
  112. // We could also solve this with the following asm function.
  113. // .CODE
  114. // GetRSP PROC
  115. // mov rax, rsp
  116. // add rax, 8
  117. // ret
  118. // GetRSP ENDP
  119. // END
  120. //
  121. static EA_NO_INLINE void* GetRSP()
  122. {
  123. #if defined(EA_COMPILER_MSVC)
  124. uintptr_t ara = (uintptr_t)_AddressOfReturnAddress();
  125. #else
  126. uintptr_t ara = (uintptr_t)__builtin_frame_address();
  127. #endif
  128. return (void*)(ara + 8);
  129. }
  130. #endif
  131. ///////////////////////////////////////////////////////////////////////////////
  132. // GetInstructionPointer
  133. //
  134. EATHREADLIB_API EA_NO_INLINE void GetInstructionPointer(void*& pInstruction)
  135. {
  136. #if defined(EA_COMPILER_MSVC)
  137. pInstruction = _ReturnAddress();
  138. #elif defined(EA_COMPILER_GNUC) || defined(EA_COMPILER_CLANG)
  139. pInstruction = __builtin_return_address(0);
  140. #else
  141. void* pReturnAddressArray[2] = { 0, 0 };
  142. GetCallstack(pReturnAddressArray, 2, NULL);
  143. pInstruction = pReturnAddressArray[1]; // This is the address of the caller.
  144. #endif
  145. }
  146. ///////////////////////////////////////////////////////////////////////////////
  147. // InitCallstack
  148. //
  149. EATHREADLIB_API void InitCallstack()
  150. {
  151. // Nothing needed.
  152. }
  153. ///////////////////////////////////////////////////////////////////////////////
  154. // ShutdownCallstack
  155. //
  156. EATHREADLIB_API void ShutdownCallstack()
  157. {
  158. // Nothing needed.
  159. }
  160. ///////////////////////////////////////////////////////////////////////////////
  161. // GetCallstack
  162. //
  163. // With the x64 (a.k.a. x86-64) platform, the CPU supports call stack tracing
  164. // natively, by design. This is as opposed to the x86 platform, in which call
  165. // stack tracing (a.k.a. unwinding) is a crap-shoot. The Win64 OS provides
  166. // two functions in particular that take care of the primary work of stack
  167. // tracing: RtlLookupFunctionEntry and RtlVirtualUnwind/RtlUnwindEx.
  168. //
  169. // On x64 each non-leaf function must have an info struct (unwind metadata) in
  170. // static memory associated with it. That info struct describes the prologue and
  171. // epilogue of the function in such a way as tell identify where its return address
  172. // is stored and how to restore non-volatile registers of the caller so that
  173. // an unwind can happen during an exception and C++ object destructors can
  174. // be called, etc. In order to implement a stack unwinding function for
  175. // Microsoft x64, you can go the old x86 route of requiring the compiler to
  176. // emit stack frames and reading the stack frame values. But that would work
  177. // only where the frames were enabled (maybe just debug builds) and wouldn't
  178. // work with third party code that didn't use the frames. But the Microsoft
  179. // x64 ABI -requires- that all non-leaf functions have the info struct
  180. // described above. And Microsoft provides the Rtl functions mentioned above
  181. // to read the info struct (RtlLookupFunctionEntry) and use it to unwind a
  182. // frame (RtlVirtualUnwind/RtlUnwindEx), whether you are in an exception or not.
  183. //
  184. // RtlVirtualUnwind implements a virtual (pretend) unwind of a stack and is
  185. // useful for reading a call stack and its unwind info without necessarily
  186. // executing an unwind (like in an exception handler). RtlVirtualUnwind provides
  187. // the infrastructure upon which higher level exception and unwind handling
  188. // support is implemented. It doesn't exist on x86, as x86 exception unwinding
  189. // is entirely done by generated C++ code and isn't in the ABI. The Virtual in
  190. // RtlVirtualUnwind has nothing to do with virtual memory, virtual functions,
  191. // or virtual disks.
  192. //
  193. // RtlUnwindEx (replaces RtlUnwind) implements an actual unwind and thus is
  194. // mostly useful only in the implementation of an exception handler and not
  195. // for doing an ad-hoc stack trace.
  196. //
  197. // You can't use RtlLookupFunctionEntry on the IP (instruction pointer) of a
  198. // leaf function, as the compiler isn't guaranteed to generate this info for
  199. // such functions. But if a leaf function calls RtlLookupFunctionEntry on its
  200. // own IP then it's no longer a leaf function and by virtue of calling RtlLookupFunctionEntry
  201. // the info will necessarily be generated by the compiler. If you want to read
  202. // the info associated with an IP of another function which may be a leaf
  203. // function, it's best to read the return address of that associated with that
  204. // function's callstack context, which is that that function's rsp register's
  205. // value as a uintptr_t* dereferenced (i.e. rsp holds the address of the
  206. // return address).
  207. //
  208. // UNWIND_HISTORY_TABLE "is used as a cache to speed up repeated exception handling lookups,
  209. // and is typically optional as far as usage with RtlUnwindEx goes – though certainly
  210. // recommended from a performance perspective." This may be useful to us, though we'd need
  211. // to make it a thread-safe static variable or similar and not a local variable.
  212. // History table declaration and preparation for use, which needs to be done per-thread:
  213. // UNWIND_HISTORY_TABLE unwindHistoryTable;
  214. // RtlZeroMemory(&unwindHistoryTable, sizeof(UNWIND_HISTORY_TABLE));
  215. // unwindHistoryTable.Unwind = TRUE;
  216. // To do: Implement usage of the history table for faster callstack tracing.
  217. //
  218. // Reading for anybody wanting to understand this:
  219. // http://www.nynaeve.net/?p=105
  220. // http://www.nynaeve.net/?p=106
  221. // http://blogs.msdn.com/b/freik/archive/2005/03/17/398200.aspx
  222. // http://www.codemachine.com/article_x64deepdive.html
  223. // http://blogs.msdn.com/b/ntdebugging/archive/2010/05/12/x64-manual-stack-reconstruction-and-stack-walking.aspx
  224. // http://eli.thegreenplace.net/2011/09/06/stack-frame-layout-on-x86-64/
  225. //
  226. EATHREADLIB_API size_t GetCallstack(void* pReturnAddressArray[], size_t nReturnAddressArrayCapacity, const CallstackContext* pContext)
  227. {
  228. CONTEXT context;
  229. PRUNTIME_FUNCTION pRuntimeFunction;
  230. ULONG64 nImageBase = 0;
  231. ULONG64 nPrevImageBase = 0;
  232. size_t nFrameIndex = 0;
  233. if(pContext)
  234. {
  235. RtlZeroMemory(&context, sizeof(context));
  236. context.Rip = pContext->mRIP;
  237. context.Rsp = pContext->mRSP;
  238. context.Rbp = pContext->mRBP;
  239. context.ContextFlags = CONTEXT_CONTROL; // CONTEXT_CONTROL actually specifies SegSs, Rsp, SegCs, Rip, and EFlags. But for callstack tracing and unwinding, all that matters is Rip and Rsp.
  240. // In the case where we are calling 0, we might be able to unwind one frame and see if we are now in a valid stack frame for
  241. // callstack generation. If not abort, otherwise we continue one frame past where the exception (calling 0) was performed
  242. if (context.Rip == 0 && context.Rsp != 0)
  243. {
  244. context.Rip = (ULONG64)(*(PULONG64)context.Rsp); // To consider: Use IsAddressReadable(pFrame) before dereferencing this pointer.
  245. context.Rsp += 8; // reset the stack pointer (+8 since we know there has been no prologue run requiring a larger number since RIP == 0)
  246. }
  247. if(context.Rip && (nFrameIndex < nReturnAddressArrayCapacity))
  248. pReturnAddressArray[nFrameIndex++] = (void*)(uintptr_t)context.Rip;
  249. }
  250. else // Else we are reading the current thread's callstack.
  251. {
  252. // To consider: Don't call the RtlCaptureContext function for EA_WINAPI_PARTITION_DESKTOP and
  253. // instead use the simpler version below it which writes Rip/Rsp/Rbp. RtlCaptureContext is much
  254. // slower. We need to verify that the 'quality' and extent of returned callstacks is good for
  255. // the simpler version before using it exclusively.
  256. #if EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP)
  257. // Apparently there is no need to memset the context struct.
  258. context.ContextFlags = CONTEXT_ALL; // Actually we should need only CONTEXT_INTEGER, so let's test that next chance we get.
  259. RtlCaptureContext(&context);
  260. #elif defined(EA_PLATFORM_XBOXONE) // This probably isn't limited to just this platform, but until we can test any other platforms we'll leave it at just this.
  261. return RtlCaptureStackBackTrace(1, (ULONG)nReturnAddressArrayCapacity, pReturnAddressArray, NULL);
  262. #else
  263. void* ip = NULL;
  264. EAGetInstructionPointer(ip);
  265. context.Rip = (uintptr_t)ip;
  266. context.Rsp = (uintptr_t)GetRSP();
  267. context.Rbp = 0; // RBP isn't actually needed for stack unwinding on x64, and don't typically need to use it in generated code, as the instruction set provides an efficient way to read/write via rsp offsets. Also, when frame pointers are omitted in the compiler settings then ebp won't be used.
  268. context.ContextFlags = CONTEXT_CONTROL;
  269. #endif
  270. }
  271. // The following loop intentionally skips the first call stack frame because
  272. // that frame corresponds this function (GetCallstack).
  273. while(context.Rip && (nFrameIndex < nReturnAddressArrayCapacity))
  274. {
  275. // Try to look up unwind metadata for the current function.
  276. nPrevImageBase = nImageBase;
  277. __try
  278. {
  279. pRuntimeFunction = (PRUNTIME_FUNCTION)RtlLookupFunctionEntry(context.Rip, &nImageBase, NULL /*&unwindHistoryTable*/);
  280. }
  281. __except (EXCEPTION_EXECUTE_HANDLER)
  282. {
  283. // Something went wrong in RtlLookupFunctionEntry, and it is unknown
  284. // if it is recoverable; so just get out.
  285. return nFrameIndex;
  286. }
  287. if(pRuntimeFunction)
  288. {
  289. // RtlVirtualUnwind is not declared in the SDK headers for non-desktop apps,
  290. // but for 64 bit targets it's always present and appears to be needed by the
  291. // existing RtlUnwindEx function. If in the end we can't use RtlVirtualUnwind
  292. // and Microsoft doesn't provide an alternative, we can implement RtlVirtualUnwind
  293. // ourselves manually (not trivial, but has the best results) or we can use
  294. // the old style stack frame following, which works only when stack frames are
  295. // enabled in the build, which usually isn't so for optimized builds and for
  296. // third party code.
  297. __try // Under at least the XBox One platform, RtlVirtualUnwind can crash here. It may possibly be due to the context being incomplete.
  298. {
  299. VOID* handlerData = NULL;
  300. ULONG64 establisherFramePointers[2] = { 0, 0 };
  301. RtlVirtualUnwind(UNW_FLAG_NHANDLER, nImageBase, context.Rip, pRuntimeFunction, &context, &handlerData, establisherFramePointers, NULL);
  302. }
  303. __except (EXCEPTION_EXECUTE_HANDLER)
  304. {
  305. context.Rip = NULL;
  306. context.ContextFlags = 0;
  307. }
  308. }
  309. else
  310. {
  311. // If we don't have a RUNTIME_FUNCTION, then we've encountered an error of some sort (mostly likely only for cases of corruption) or leaf function (which doesn't make sense, given that we are moving up in the call sequence). Adjust the stack appropriately.
  312. context.Rip = (ULONG64)(*(PULONG64)context.Rsp); // To consider: Use IsAddressReadable(pFrame) before dereferencing this pointer.
  313. context.Rsp += 8;
  314. }
  315. if(context.Rip)
  316. {
  317. if(nFrameIndex < nReturnAddressArrayCapacity)
  318. pReturnAddressArray[nFrameIndex++] = (void*)(uintptr_t)context.Rip;
  319. }
  320. }
  321. return nFrameIndex;
  322. }
  323. ///////////////////////////////////////////////////////////////////////////////
  324. // GetThreadIdFromThreadHandle
  325. //
  326. // This implementation is the same as the one in EAThread.
  327. // Converts a thread HANDLE (threadId) to a thread id DWORD (sysThreadId).
  328. // Recall that Windows has two independent thread identifier types.
  329. //
  330. EATHREADLIB_API uint32_t GetThreadIdFromThreadHandle(intptr_t threadId)
  331. {
  332. // Win64 has this function natively, unlike earlier versions of 32 bit Windows.
  333. return (uint32_t)::GetThreadId((HANDLE)threadId);
  334. }
  335. ///////////////////////////////////////////////////////////////////////////////
  336. // GetCallstackContext
  337. //
  338. // The threadId is the same thing as the Windows' HANDLE GetCurrentThread() function
  339. // and not the same thing as Windows' GetCurrentThreadId function. See the
  340. // GetCallstackContextSysThreadId for the latter.
  341. //
  342. #if EA_USE_CPP11_CONCURRENCY
  343. EATHREADLIB_API bool GetCallstackContext(CallstackContext& context, EA::Thread::ThreadId threadId)
  344. {
  345. // Retrieve the Windows thread identifier from the std::thread::id structure.
  346. // This is unavoidable because GetCallstackContextSysThreadId compares the value of 'sysThreadId'
  347. // against data from the Windows API function 'GetCurrentThreadId' which returns a Windows thread identifier.
  348. // http://msdn.microsoft.com/en-us/library/windows/desktop/ms683183(v=vs.85).aspx
  349. static_assert(sizeof(_Thrd_t) == sizeof(threadId), "We expect the 'std::thread::id' to have a single member of type '_Thrd_t'.");
  350. _Thrd_t wThrd;
  351. memcpy(&wThrd, &threadId, sizeof(wThrd)); // we use memcpy to avoid strict aliasing issues caused by casting to access internal members.
  352. return GetCallstackContextSysThreadId(context, _Thr_val(wThrd));
  353. }
  354. #else
  355. EATHREADLIB_API bool GetCallstackContext(CallstackContext& context, intptr_t threadId)
  356. {
  357. if((threadId == (intptr_t)kThreadIdInvalid) || (threadId == (intptr_t)kThreadIdCurrent))
  358. threadId = (intptr_t)::GetCurrentThread(); // GetCurrentThread returns a thread 'pseudohandle' and not a real thread handle.
  359. const DWORD sysThreadId = EA::Thread::GetThreadIdFromThreadHandle(threadId);
  360. return GetCallstackContextSysThreadId(context, sysThreadId);
  361. }
  362. #endif
  363. ///////////////////////////////////////////////////////////////////////////////
  364. // GetCallstackContextSysThreadId
  365. //
  366. // A sysThreadId is a Microsoft DWORD thread id, which can be obtained from
  367. // the currently running thread via GetCurrentThreadId. It can be obtained from
  368. // a Microsoft thread HANDLE via EA::Thread::GetThreadIdFromThreadHandle();
  369. // A DWORD thread id can be converted to a thread HANDLE via the Microsoft OpenThread
  370. // system function.
  371. //
  372. EA_DISABLE_VC_WARNING(4701) // potentially uninitialized local variable 'win64CONTEXT' used
  373. EATHREADLIB_API bool GetCallstackContextSysThreadId(CallstackContext& context, intptr_t sysThreadId)
  374. {
  375. EAT_COMPILETIME_ASSERT(offsetof(EA::Thread::ContextX86_64, Rip) == offsetof(CONTEXT, Rip));
  376. EAT_COMPILETIME_ASSERT(offsetof(EA::Thread::ContextX86_64, VectorRegister) == offsetof(CONTEXT, VectorRegister));
  377. EAT_COMPILETIME_ASSERT(offsetof(EA::Thread::ContextX86_64, LastExceptionFromRip) == offsetof(CONTEXT, LastExceptionFromRip));
  378. const DWORD sysThreadIdCurrent = GetCurrentThreadId();
  379. CONTEXT win64CONTEXT;
  380. if(sysThreadIdCurrent == (DWORD)sysThreadId) // If getting the context of the current thread...
  381. {
  382. #if EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP)
  383. RtlCaptureContext(&win64CONTEXT); // This function has no return value.
  384. #else
  385. void* ip = NULL;
  386. EAGetInstructionPointer(ip);
  387. win64CONTEXT.Rip = (uintptr_t)ip;
  388. win64CONTEXT.Rsp = (uintptr_t)GetRSP();
  389. win64CONTEXT.Rbp = 0; // RBP isn't actually needed for stack unwinding on x64, and don't typically need to use it in generated code, as the instruction set provides an efficient way to read/write via rsp offsets. Also, when frame pointers are omitted in the compiler settings then ebp won't be used.
  390. win64CONTEXT.ContextFlags = CONTEXT_CONTROL; // CONTEXT_CONTROL actually specifies SegSs, Rsp, SegCs, Rip, and EFlags. But for callstack tracing and unwinding, all that matters is Rip and Rsp.
  391. #endif
  392. }
  393. else
  394. {
  395. #if !defined(EA_PLATFORM_WINDOWS) || EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP)
  396. // In this case we are working with a separate thread, so we suspend it
  397. // and read information about it and then resume it. We cannot use this
  398. // technique to get the context of the current thread unless we do it by
  399. // spawing a new thread which suspends this thread and calls GetThreadContext.
  400. HANDLE threadId = OpenThread(THREAD_SUSPEND_RESUME | THREAD_GET_CONTEXT, TRUE, (DWORD)sysThreadId);
  401. BOOL result = false;
  402. EAT_ASSERT(threadId != 0); // If this fails then maybe there's a process security restriction we are running into.
  403. if(threadId)
  404. {
  405. DWORD suspendResult = SuspendThread(threadId);
  406. if(suspendResult != (DWORD)-1)
  407. {
  408. win64CONTEXT.ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER;
  409. result = GetThreadContext(threadId, &win64CONTEXT);
  410. suspendResult = ResumeThread(threadId);
  411. EAT_ASSERT(suspendResult != (DWORD)-1); EA_UNUSED(suspendResult);
  412. }
  413. CloseHandle(threadId);
  414. }
  415. if(!result)
  416. {
  417. win64CONTEXT.Rip = 0;
  418. win64CONTEXT.Rsp = 0;
  419. win64CONTEXT.Rbp = 0;
  420. win64CONTEXT.ContextFlags = 0;
  421. }
  422. #endif
  423. }
  424. context.mRIP = win64CONTEXT.Rip;
  425. context.mRSP = win64CONTEXT.Rsp;
  426. context.mRBP = win64CONTEXT.Rbp;
  427. return (context.mRIP != 0);
  428. }
  429. EA_RESTORE_VC_WARNING()
  430. ///////////////////////////////////////////////////////////////////////////////
  431. // GetCallstackContext
  432. //
  433. void GetCallstackContext(CallstackContext& context, const Context* pContext)
  434. {
  435. context.mRIP = pContext->Rip;
  436. context.mRSP = pContext->Rsp;
  437. context.mRBP = pContext->Rbp;
  438. }
  439. ///////////////////////////////////////////////////////////////////////////////
  440. // GetModuleFromAddress
  441. //
  442. size_t GetModuleFromAddress(const void* address, char* pModuleName, size_t moduleNameCapacity)
  443. {
  444. MEMORY_BASIC_INFORMATION mbi;
  445. if(VirtualQuery(address, &mbi, sizeof(mbi)))
  446. {
  447. HMODULE hModule = (HMODULE)mbi.AllocationBase;
  448. if(hModule)
  449. {
  450. #if EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP) // GetModuleFileName is desktop API-only.
  451. // As of the early Windows 8 SDKs, GetModuleFileName is not exposed to non-desktop
  452. // apps, though it's apparently nevertheless present in the libraries.
  453. return GetModuleFileNameA(hModule, pModuleName, (DWORD)moduleNameCapacity);
  454. #else
  455. // If it turns out in the end that we really can't do this, then for non-shipping builds
  456. // we can likely implement a manual version of this via information found through the
  457. // TEB structure for the process.
  458. return GetModuleFileNameA(hModule, pModuleName, (DWORD)moduleNameCapacity);
  459. #endif
  460. }
  461. }
  462. pModuleName[0] = 0;
  463. return 0;
  464. }
  465. ///////////////////////////////////////////////////////////////////////////////
  466. // GetModuleHandleFromAddress
  467. //
  468. // The input pAddress must be an address of code and not data or stack space.
  469. //
  470. EATHREADLIB_API ModuleHandle GetModuleHandleFromAddress(const void* pAddress)
  471. {
  472. MEMORY_BASIC_INFORMATION mbi;
  473. if(VirtualQuery(pAddress, &mbi, sizeof(mbi)))
  474. {
  475. // In Microsoft platforms, the module handle is really just a pointer
  476. // to the code for the module. It corresponds directly to the information
  477. // in the map file, though the actual address may have been changed
  478. // from the value in the map file on loading into memory.
  479. return (ModuleHandle)mbi.AllocationBase;
  480. }
  481. return 0;
  482. }
  483. ///////////////////////////////////////////////////////////////////////////////
  484. // SetStackBase
  485. //
  486. EATHREADLIB_API void SetStackBase(void* /*pStackBase*/)
  487. {
  488. // Nothing to do, as GetStackBase always works on its own.
  489. }
  490. ///////////////////////////////////////////////////////////////////////////////
  491. // GetStackBase
  492. //
  493. EATHREADLIB_API void* GetStackBase()
  494. {
  495. NT_TIB64* pTIB = (NT_TIB64*)NtCurrentTeb(); // NtCurrentTeb is defined in <WinNT.h> as an inline call to __readgsqword
  496. return (void*)pTIB->StackBase;
  497. }
  498. ///////////////////////////////////////////////////////////////////////////////
  499. // GetStackLimit
  500. //
  501. EATHREADLIB_API void* GetStackLimit()
  502. {
  503. NT_TIB64* pTIB = (NT_TIB64*)NtCurrentTeb(); // NtCurrentTeb is defined in <WinNT.h> as an inline call to __readgsqword
  504. return (void*)pTIB->StackLimit;
  505. // The following is an alternative implementation that returns the extent
  506. // of the current stack usage as opposed to the stack limit as seen by the OS.
  507. // This value will be a higher address than Tib.StackLimit (recall that the
  508. // stack grows downward). It's debatable which of these two approaches is
  509. // better, as one returns the thread's -usable- stack space while the
  510. // other returns how much the thread is -currently- using. The determination
  511. // of the usable stack space is complicated by the fact that Microsoft
  512. // platforms auto-extend the stack if the process pushes beyond the current limit.
  513. // In the end the Tib.StackLimit solution is actually the most portable across
  514. // Microsoft OSs and compilers for those OSs (Microsoft or not).
  515. // Alternative implementation:
  516. // We return our stack pointer, which is a good approximation of the stack limit of the caller.
  517. // void* rsp = GetRSP();
  518. // return rsp;
  519. }
  520. } // namespace Thread
  521. } // namespace EA
  522. #if defined(EA_COMPILER_MSVC) && defined(EA_PLATFORM_WIN64)
  523. #pragma optimize("", on) // See comments above regarding this optimization change.
  524. #endif