eathread_callstack_libunwind.cpp 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687
  1. ///////////////////////////////////////////////////////////////////////////////
  2. // Copyright (c) Electronic Arts Inc. All rights reserved.
  3. ///////////////////////////////////////////////////////////////////////////////
  4. #include <EABase/eabase.h>
  5. #include <eathread/eathread.h>
  6. #include <eathread/eathread_atomic.h>
  7. #include <eathread/eathread_callstack.h>
  8. #include <eathread/eathread_callstack_context.h>
  9. #include <eathread/eathread_storage.h>
  10. #include <string.h>
  11. #include <pthread.h>
  12. #include <unistd.h>
  13. #include <unwind.h>
  14. #if defined(EA_PLATFORM_BSD)
  15. #include <sys/signal.h>
  16. #include <machine/signal.h>
  17. #elif defined(EA_PLATFORM_LINUX)
  18. #include <signal.h>
  19. #endif
  20. namespace EA
  21. {
  22. namespace Thread
  23. {
  24. ///////////////////////////////////////////////////////////////////////////////
  25. // InitCallstack
  26. //
  27. EATHREADLIB_API void InitCallstack()
  28. {
  29. // Nothing needed.
  30. }
  31. ///////////////////////////////////////////////////////////////////////////////
  32. // ShutdownCallstack
  33. //
  34. EATHREADLIB_API void ShutdownCallstack()
  35. {
  36. // Nothing needed.
  37. }
  38. EATHREADLIB_API void GetInstructionPointer(void*& p)
  39. {
  40. // Currently all platforms that have <unwind.h> have __builtin_return_address().
  41. p = __builtin_return_address(0);
  42. }
  43. // This is a callback function which libunwind calls, once per callstack entry.
  44. /*
  45. Linux for ARM:
  46. enum _Unwind_Reason_Code {
  47. _URC_OK = 0, // operation completed successfully
  48. _URC_FOREIGN_EXCEPTION_CAUGHT = 1,
  49. _URC_END_OF_STACK = 5,
  50. _URC_HANDLER_FOUND = 6,
  51. _URC_INSTALL_CONTEXT = 7,
  52. _URC_CONTINUE_UNWIND = 8,
  53. _URC_FAILURE = 9 // unspecified failure of some kind
  54. };
  55. #define _URC_NO_REASON _URC_OK
  56. BSD (and I think also Linux for x86/x64):
  57. enum _Unwind_Reason_Code {
  58. _URC_NO_REASON = 0,
  59. _URC_FOREIGN_EXCEPTION_CAUGHT = 1,
  60. _URC_FATAL_PHASE2_ERROR = 2,
  61. _URC_FATAL_PHASE1_ERROR = 3,
  62. _URC_NORMAL_STOP = 4,
  63. _URC_END_OF_STACK = 5,
  64. _URC_HANDLER_FOUND = 6,
  65. _URC_INSTALL_CONTEXT = 7,
  66. _URC_CONTINUE_UNWIND = 8
  67. };
  68. */
  69. struct UnwindCallbackContext
  70. {
  71. void** mpReturnAddressArray;
  72. size_t mReturnAddressArrayCapacity;
  73. size_t mReturnAddressArrayIndex;
  74. };
  75. static _Unwind_Reason_Code UnwindCallback(_Unwind_Context* pUnwindContext, void* pUnwindCallbackContextVoid)
  76. {
  77. UnwindCallbackContext* pUnwindCallbackContext = (UnwindCallbackContext*)pUnwindCallbackContextVoid;
  78. if(pUnwindCallbackContext->mReturnAddressArrayIndex < pUnwindCallbackContext->mReturnAddressArrayCapacity)
  79. {
  80. uintptr_t ip = _Unwind_GetIP(pUnwindContext);
  81. pUnwindCallbackContext->mpReturnAddressArray[pUnwindCallbackContext->mReturnAddressArrayIndex++] = (void*)ip;
  82. return _URC_NO_REASON;
  83. }
  84. #if defined(EA_PLATFORM_LINUX)
  85. return _URC_NO_REASON; // Is there a way to tell the caller that we want to stop?
  86. #else
  87. return _URC_NORMAL_STOP;
  88. #endif
  89. }
  90. /*
  91. The following commented-out code is for reading the callstack of a thread other than the current one.
  92. The code below is originally for BSD Unix, and probably needs to be tweaked to support Linux.
  93. namespace Local
  94. {
  95. enum EAThreadBacktraceState
  96. {
  97. // Positive thread lwp ids are here implicitly.
  98. EATHREAD_BACKTRACE_STATE_NONE = -1,
  99. EATHREAD_BACKTRACE_STATE_DUMPING = -2,
  100. EATHREAD_BACKTRACE_STATE_DONE = -3,
  101. EATHREAD_BACKTRACE_STATE_CANCEL = -4
  102. };
  103. struct ThreadBacktraceState
  104. {
  105. EA::Thread::AtomicInt32 mState; // One of enum EAThreadBacktraceState or (initially) the thread id of the thread we are targeting.
  106. void** mCallstack; // Output param
  107. size_t mCallstackCapacity; // Input param, refers to array capacity of mCallstack.
  108. size_t mCallstackCount; // Output param
  109. pthread_t mPthread; // Output param
  110. ThreadBacktraceState() : mState(EATHREAD_BACKTRACE_STATE_NONE), mCallstackCapacity(0), mCallstackCount(0), mPthread(NULL){}
  111. };
  112. static pthread_mutex_t gThreadBacktraceMutex = PTHREAD_MUTEX_INITIALIZER;
  113. static ThreadBacktraceState gThreadBacktraceState; // Protected by gThreadBacktraceMutex.
  114. static void gThreadBacktraceSignalHandler(int sigNum, siginfo_t* pSigInfo, void* pSigContextVoid)
  115. {
  116. int32_t lwpSelf = *(int32_t*)pthread_self();
  117. if(gThreadBacktraceState.mState.SetValueConditional(EATHREAD_BACKTRACE_STATE_DUMPING, lwpSelf))
  118. {
  119. gThreadBacktraceState.mPthread = pthread_self();
  120. if(gThreadBacktraceState.mCallstackCapacity)
  121. {
  122. gThreadBacktraceState.mCallstackCount = GetCallstack(gThreadBacktraceState.mCallstack, gThreadBacktraceState.mCallstackCapacity, (const CallstackContext*)NULL);
  123. // At this point we need to remove the top 6 entries and insert an entry for where the thread's instruction pointer is.
  124. if(gThreadBacktraceState.mCallstackCount >= 6) // This should always be true.
  125. {
  126. gThreadBacktraceState.mCallstackCount -= 5;
  127. memmove(&gThreadBacktraceState.mCallstack[1], &gThreadBacktraceState.mCallstack[6], (gThreadBacktraceState.mCallstackCount - 1) * sizeof(void*));
  128. }
  129. else
  130. gThreadBacktraceState.mCallstackCount = 1;
  131. gThreadBacktraceState.mCallstack[0] = pSigContextVoid ? reinterpret_cast<void*>(reinterpret_cast<sigcontext*>((uintptr_t)pSigContextVoid + 48)->sc_rip) : NULL;
  132. }
  133. else
  134. gThreadBacktraceState.mCallstackCount = 0;
  135. gThreadBacktraceState.mState.SetValue(EATHREAD_BACKTRACE_STATE_DONE);
  136. }
  137. // else this thread received an unexpected SIGURG. This can happen if it was so delayed that
  138. // we timed out waiting for it to happen and moved on.
  139. }
  140. }
  141. /// GetCallstack
  142. ///
  143. /// This is a version of GetCallstack which gets the callstack of a thread based on its thread id as opposed to
  144. /// its register state. It works by injecting a signal handler into the given thread and reading the self callstack
  145. /// then exiting from the signal handler. The GetCallstack function sets this up, generates the signal for the
  146. /// other thread, then waits for it to complete. It uses the SIGURG signal for this.
  147. ///
  148. /// Primary causes of failure:
  149. /// The target thread has SIGURG explicitly ignored.
  150. /// The target thread somehow is getting too little CPU time to respond to the signal.
  151. ///
  152. /// To do: Change this function to take a ThreadInfo as a last parameter instead of pthread_t. And have the
  153. /// ThreadInfo return additional basic thread information. Or maybe even change this function to be a
  154. /// GetThreadInfo function instead of GetCallstack.
  155. ///
  156. EATHREADLIB_API size_t GetCallstack(void* pReturnAddressArray[], size_t nReturnAddressArrayCapacity, pthread_t& pthread)
  157. {
  158. using namespace Local;
  159. size_t callstackCount = 0;
  160. if(pthread)
  161. {
  162. pthread_t pthreadSelf = pthread_self();
  163. int32_t lwp = *(int32_t*)pthread;
  164. int32_t lwpSelf = *(int32_t*)pthreadSelf;
  165. if(lwp == lwpSelf) // This function can be called only for a thread other than self.
  166. callstackCount = GetCallstack(pReturnAddressArray, nReturnAddressArrayCapacity, (const CallstackContext*)NULL);
  167. else
  168. {
  169. struct sigaction act; memset(&act, 0, sizeof(act));
  170. struct sigaction oact; memset(&oact, 0, sizeof(oact));
  171. act.sa_sigaction = gThreadBacktraceSignalHandler;
  172. act.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK;
  173. pthread_mutex_lock(&gThreadBacktraceMutex);
  174. if(sigaction(SIGURG, &act, &oact) == 0)
  175. {
  176. gThreadBacktraceState.mCallstack = pReturnAddressArray;
  177. gThreadBacktraceState.mCallstackCapacity = nReturnAddressArrayCapacity;
  178. gThreadBacktraceState.mState.SetValue(lwp);
  179. // Signal the specific thread that we want to dump.
  180. int32_t stateTemp = lwp;
  181. if(pthread_kill(pthread, SIGURG) == 0)
  182. {
  183. // Wait for the other thread to start dumping the stack, or time out.
  184. for(int waitMS = 200; waitMS; waitMS--)
  185. {
  186. stateTemp = gThreadBacktraceState.mState.GetValue();
  187. if(stateTemp != lwp)
  188. break;
  189. usleep(1000); // This sleep gives the OS the opportunity to execute the target thread, even if it's of a lower priority than this thread.
  190. }
  191. }
  192. // else apparently failed to send SIGURG to the thread, or the thread was paused in a way that it couldn't receive it.
  193. if(stateTemp == lwp) // If the operation timed out or seemingly never started...
  194. {
  195. if(gThreadBacktraceState.mState.SetValueConditional(EATHREAD_BACKTRACE_STATE_CANCEL, lwp)) // If the backtrace still didn't start, and we were able to stop it by setting the state to cancel...
  196. stateTemp = EATHREAD_BACKTRACE_STATE_CANCEL;
  197. else
  198. stateTemp = gThreadBacktraceState.mState.GetValue(); // It looks like the backtrace thread did in fact get a late start and is now executing
  199. }
  200. // Wait indefinitely for the dump to finish or be canceled.
  201. // We cannot apply a timeout here because the other thread is accessing state that
  202. // is owned by this thread.
  203. for(int waitMS = 100; (stateTemp == EATHREAD_BACKTRACE_STATE_DUMPING) && waitMS; waitMS--) // If the thread is (still) busy writing it out its callstack...
  204. {
  205. usleep(1000);
  206. stateTemp = gThreadBacktraceState.mState.GetValue();
  207. }
  208. if(stateTemp == EATHREAD_BACKTRACE_STATE_DONE)
  209. callstackCount = gThreadBacktraceState.mCallstackCount;
  210. // Else give up on it. It's OK to just fall through.
  211. // Restore the original SIGURG handler.
  212. sigaction(SIGURG, &oact, NULL);
  213. }
  214. pthread_mutex_unlock(&gThreadBacktraceMutex);
  215. }
  216. }
  217. return callstackCount;
  218. }
  219. */
  220. ///////////////////////////////////////////////////////////////////////////////
  221. // GetCallstack
  222. //
  223. EATHREADLIB_API size_t GetCallstack(void* pReturnAddressArray[], size_t nReturnAddressArrayCapacity, const CallstackContext* pContext)
  224. {
  225. // libunwind can only read the stack from the current thread.
  226. // However, we can accomplish this for another thread by injecting a signal handler into that thread.
  227. // See the EAThreadBacktrace() function source code above.
  228. if(pContext == NULL) // If reading the current thread's context...
  229. {
  230. UnwindCallbackContext context = { pReturnAddressArray, nReturnAddressArrayCapacity, 0 };
  231. _Unwind_Backtrace(&UnwindCallback, &context);
  232. if (context.mReturnAddressArrayIndex > 0)
  233. {
  234. --context.mReturnAddressArrayIndex; // Remove the first entry, because it refers to this function and by design we don't include this function.
  235. memmove(pReturnAddressArray, pReturnAddressArray + 1, context.mReturnAddressArrayIndex * sizeof(void*));
  236. }
  237. return context.mReturnAddressArrayIndex;
  238. }
  239. // We don't yet have a means to read another thread's callstack via only the CallstackContext.
  240. return 0;
  241. }
  242. ///////////////////////////////////////////////////////////////////////////////
  243. // GetCallstackContext
  244. //
  245. EATHREADLIB_API bool GetCallstackContext(CallstackContext& context, intptr_t threadId)
  246. {
  247. // True Linux-based ARM platforms (usually tablets and phones) can use pthread_attr_getstack.
  248. #if defined(EA_PLATFORM_ANDROID)
  249. if((threadId == (intptr_t)kThreadIdInvalid) ||
  250. (threadId == (intptr_t)kThreadIdCurrent) ||
  251. (threadId == (intptr_t)EA::Thread::GetThreadId()))
  252. {
  253. // Note: the behavior below is inconsistent between platforms and needs to be made so.
  254. #if defined(__ARMCC_VERSION) // If using the ARM Compiler...
  255. context.mSP = (uint32_t)__current_sp();
  256. context.mLR = (uint32_t)__return_address();
  257. context.mPC = (uint32_t)__current_pc();
  258. context.mStackPointer = context.mSP;
  259. #elif defined(__GNUC__) || defined(EA_COMPILER_CLANG)
  260. #if defined(EA_PROCESSOR_X86_64)
  261. context.mRIP = (uint64_t)__builtin_return_address(0);
  262. context.mRSP = (uint64_t)__builtin_frame_address(1);
  263. context.mRBP = 0;
  264. context.mStackPointer = context.mRSP;
  265. #elif defined(EA_PROCESSOR_X86)
  266. context.mEIP = (uint32_t)__builtin_return_address(0);
  267. context.mESP = (uint32_t)__builtin_frame_address(1);
  268. context.mEBP = 0;
  269. context.mStackPointer = context.mESP;
  270. #elif defined(EA_PROCESSOR_ARM32)
  271. // register uintptr_t current_sp asm ("sp");
  272. context.mSP = (uint32_t)__builtin_frame_address(0);
  273. context.mLR = (uint32_t)__builtin_return_address(0);
  274. void* pInstruction;
  275. EAGetInstructionPointer(pInstruction);
  276. context.mPC = reinterpret_cast<uintptr_t>(pInstruction);
  277. context.mStackPointer = context.mSP;
  278. #elif defined(EA_PROCESSOR_ARM64)
  279. // register uintptr_t current_sp asm ("sp");
  280. context.mSP = (uint64_t)__builtin_frame_address(0);
  281. context.mLR = (uint64_t)__builtin_return_address(0);
  282. void* pInstruction;
  283. EAGetInstructionPointer(pInstruction);
  284. context.mPC = reinterpret_cast<uintptr_t>(pInstruction);
  285. context.mStackPointer = context.mSP;
  286. #endif
  287. #endif
  288. context.mStackBase = (uintptr_t)GetStackBase();
  289. context.mStackLimit = (uintptr_t)GetStackLimit();
  290. return true;
  291. }
  292. else
  293. {
  294. // Else haven't implemented getting the stack info for other threads
  295. memset(&context, 0, sizeof(context));
  296. return false;
  297. }
  298. #else
  299. pthread_t self = pthread_self();
  300. pthread_t pthreadId = (typeof(pthread_t))threadId; // Requires that pthread_t is a pointer or integral type.
  301. if(pthread_equal(pthreadId, self))
  302. {
  303. void* pInstruction;
  304. // This is some crazy GCC code that happens to work:
  305. pInstruction = ({ __label__ label; label: &&label; });
  306. // Note: the behavior below is inconsistent between platforms and needs to be made so.
  307. #if defined(EA_PROCESSOR_X86_64)
  308. context.mRIP = (uint64_t)pInstruction;
  309. context.mRSP = (uint64_t)__builtin_frame_address(1);
  310. context.mRBP = 0;
  311. #elif defined(EA_PROCESSOR_X86)
  312. context.mEIP = (uint32_t)__builtin_return_address(0);
  313. context.mESP = (uint32_t)__builtin_frame_address(1);
  314. context.mEBP = 0;
  315. #endif
  316. return true;
  317. }
  318. else
  319. {
  320. // There is currently no way to do this.
  321. memset(&context, 0, sizeof(context));
  322. return false;
  323. }
  324. #endif
  325. }
  326. ///////////////////////////////////////////////////////////////////////////////
  327. // GetCallstackContextSysThreadId
  328. //
  329. EATHREADLIB_API bool GetCallstackContextSysThreadId(CallstackContext& context, intptr_t sysThreadId)
  330. {
  331. // Assuming we are using pthreads, sysThreadId == threadId.
  332. return GetCallstackContext(context, sysThreadId);
  333. }
  334. ///////////////////////////////////////////////////////////////////////////////
  335. // GetCallstackContext
  336. //
  337. EATHREADLIB_API void GetCallstackContext(CallstackContext& context, const Context* pContext)
  338. {
  339. #if defined(EA_PROCESSOR_X86_64)
  340. context.mRIP = pContext->Rip;
  341. context.mRSP = pContext->Rsp;
  342. context.mRBP = pContext->Rbp;
  343. #elif defined(EA_PROCESSOR_X86)
  344. context.mEIP = pContext->Eip;
  345. context.mESP = pContext->Esp;
  346. context.mEBP = pContext->Ebp;
  347. #elif defined(EA_PROCESSOR_ARM32)
  348. context.mSP = pContext->mGpr[13];
  349. context.mLR = pContext->mGpr[14];
  350. context.mPC = pContext->mGpr[15];
  351. #elif defined(EA_PROCESSOR_ARM64)
  352. context.mSP = pContext->mGpr[31];
  353. context.mLR = pContext->mGpr[30];
  354. context.mPC = pContext->mPC;
  355. #else
  356. // To do.
  357. #endif
  358. }
  359. ///////////////////////////////////////////////////////////////////////////////
  360. // GetModuleFromAddress
  361. //
  362. // Returns the required strlen of pModuleName.
  363. //
  364. EATHREADLIB_API size_t GetModuleFromAddress(const void* address, char* pModuleName, size_t moduleNameCapacity)
  365. {
  366. #if 0 // Disabled until testable: defined(EA_PLATFORM_LINUX)
  367. // The output of reading /proc/self/maps is like the following (there's no leading space on each line).
  368. // We look for entries that have r-x as the first three flags, as they are executable modules.
  369. // The format is (http://linux.die.net/man/5/proc):
  370. // <begin address>-<end address> <flags> <offset> <device major>:<device minor> <inode> <path>
  371. //
  372. // 00400000-0040b000 r-xp 00000000 08:01 655382 /bin/cat
  373. // 0060a000-0060b000 r--p 0000a000 08:01 655382 /bin/cat
  374. // 0060b000-0060c000 rw-p 0000b000 08:01 655382 /bin/cat
  375. // 0060c000-0062d000 rw-p 00000000 00:00 0 [heap]
  376. // 7ffff77b5000-7ffff7a59000 r--p 00000000 08:01 395618 /usr/lib/locale/locale-archive
  377. // 7ffff7a59000-7ffff7bd3000 r-xp 00000000 08:01 1062643 /lib/libc-2.12.1.so
  378. // 7ffff7bd3000-7ffff7dd2000 ---p 0017a000 08:01 1062643 /lib/libc-2.12.1.so
  379. // 7ffff7dd2000-7ffff7dd6000 r--p 00179000 08:01 1062643 /lib/libc-2.12.1.so
  380. // 7ffff7dd6000-7ffff7dd7000 rw-p 0017d000 08:01 1062643 /lib/libc-2.12.1.so
  381. // 7ffff7dd7000-7ffff7ddc000 rw-p 00000000 00:00 0
  382. // 7ffff7ddc000-7ffff7dfc000 r-xp 00000000 08:01 1062651 /lib/ld-2.12.1.so
  383. // 7ffff7fd9000-7ffff7fdc000 rw-p 00000000 00:00 0
  384. // 7ffff7ff9000-7ffff7ffb000 rw-p 00000000 00:00 0
  385. // 7ffff7ffb000-7ffff7ffc000 r-xp 00000000 00:00 0 [vdso]
  386. // 7ffff7ffc000-7ffff7ffd000 r--p 00020000 08:01 1062651 /lib/ld-2.12.1.so
  387. // 7ffff7ffd000-7ffff7ffe000 rw-p 00021000 08:01 1062651 /lib/ld-2.12.1.so
  388. // 7ffff7ffe000-7ffff7fff000 rw-p 00000000 00:00 0
  389. // 7ffffffde000-7ffffffff000 rw-p 00000000 00:00 0 [stack]
  390. // ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]
  391. FILE* file = fopen("/proc/self/maps", "rt");
  392. if(file)
  393. {
  394. uint64_t address64 = (uint64_t)reinterpret_cast<uintptr_t>(address);
  395. char lineBuffer[1024];
  396. while(fgets(lineBuffer, sizeof(lineBuffer), file) != NULL)
  397. {
  398. size_t lineLength = strlen(lineBuffer);
  399. if((lineLength > 0) && (lineBuffer[lineLength - 1] == '\n'))
  400. lineBuffer[--lineLength] = '\0';
  401. uint64_t start, end, offset, devMajor, devMinor, inode;
  402. char flags[4];
  403. char path[512 + 1];
  404. // 7ffff7ddc000-7ffff7dfc000 r-xp 00000000 08:01 1062651 /lib/ld-2.12.1.so
  405. int fieldCount = EA::StdC::Sscanf(lineBuffer, "%I64x-%I64x %c%c%c%c %I64x %I64d:%I64d %I64x %512s",
  406. &start, &end, &flags[0], &flags[1], &flags[2], &flags[3], &offset,
  407. &devMajor, &devMinor, &inode, path);
  408. if(fieldCount == 11)
  409. {
  410. if((flags[0] == 'r') && (flags[1] == '-') && (flags[2] == 'x')) // If this looks like an executable module...
  411. {
  412. if((address64 >= start) && (address64 < end)) // If this is the module that corresponds to the input address
  413. {
  414. // We can't strcpy path as-is because it might be truncated due to spaces in the file name.
  415. // So we get the location path is in the original lineBuffer and strcpy everything till the end.
  416. char* pPathBegin = EA::StdC::Strstr(lineBuffer, path);
  417. return EA::StdC::Strlcpy(pModuleName, pPathBegin, moduleNameCapacity);
  418. }
  419. }
  420. }
  421. }
  422. fclose(file);
  423. }
  424. #else
  425. EA_UNUSED(address);
  426. // Probably also doable for BSD.
  427. // http://freebsd.1045724.n5.nabble.com/How-to-get-stack-bounds-of-current-process-td4053477.html
  428. #endif
  429. if(moduleNameCapacity > 0)
  430. pModuleName[0] = 0;
  431. return 0;
  432. }
  433. /*
  434. uint64_t GetLibraryAddressLinux(const char* pModuleName)
  435. {
  436. // The output of reading /proc/self/maps is like the following (there's no leading space on each line).
  437. // We look for entries that have r-x as the first three flags, as they are executable modules.
  438. // The format is (http://linux.die.net/man/5/proc):
  439. // <begin address>-<end address> <flags> <offset> <device major>:<device minor> <inode> <path>
  440. //
  441. // 00400000-0040b000 r-xp 00000000 08:01 655382 /bin/cat
  442. // 0060a000-0060b000 r--p 0000a000 08:01 655382 /bin/cat
  443. // 0060b000-0060c000 rw-p 0000b000 08:01 655382 /bin/cat
  444. // 0060c000-0062d000 rw-p 00000000 00:00 0 [heap]
  445. // 7ffff77b5000-7ffff7a59000 r--p 00000000 08:01 395618 /usr/lib/locale/locale-archive
  446. // 7ffff7a59000-7ffff7bd3000 r-xp 00000000 08:01 1062643 /lib/libc-2.12.1.so
  447. // 7ffff7bd3000-7ffff7dd2000 ---p 0017a000 08:01 1062643 /lib/libc-2.12.1.so
  448. // 7ffff7dd2000-7ffff7dd6000 r--p 00179000 08:01 1062643 /lib/libc-2.12.1.so
  449. // 7ffff7dd6000-7ffff7dd7000 rw-p 0017d000 08:01 1062643 /lib/libc-2.12.1.so
  450. // 7ffff7dd7000-7ffff7ddc000 rw-p 00000000 00:00 0
  451. // 7ffff7ddc000-7ffff7dfc000 r-xp 00000000 08:01 1062651 /lib/ld-2.12.1.so
  452. // 7ffff7fd9000-7ffff7fdc000 rw-p 00000000 00:00 0
  453. // 7ffff7ff9000-7ffff7ffb000 rw-p 00000000 00:00 0
  454. // 7ffff7ffb000-7ffff7ffc000 r-xp 00000000 00:00 0 [vdso]
  455. // 7ffff7ffc000-7ffff7ffd000 r--p 00020000 08:01 1062651 /lib/ld-2.12.1.so
  456. // 7ffff7ffd000-7ffff7ffe000 rw-p 00021000 08:01 1062651 /lib/ld-2.12.1.so
  457. // 7ffff7ffe000-7ffff7fff000 rw-p 00000000 00:00 0
  458. // 7ffffffde000-7ffffffff000 rw-p 00000000 00:00 0 [stack]
  459. // ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]
  460. uint64_t baseAddress = 0;
  461. FILE* file = fopen("/proc/self/maps", "rt");
  462. if(file)
  463. {
  464. size_t moduleNameLength = strlen(pModuleName);
  465. char lineBuffer[512];
  466. while(fgets(lineBuffer, sizeof lineBuffer, file) != NULL)
  467. {
  468. size_t lineLength = strlen(lineBuffer);
  469. if((lineLength > 0) && (lineBuffer[lineLength - 1] == '\n'))
  470. lineBuffer[--lineLength] = '\0';
  471. if((lineLength >= moduleNameLength) &&
  472. memcmp(lineBuffer + lineLength - moduleNameLength, pModuleName, moduleNameLength) == 0)
  473. {
  474. uint64_t start, end, offset;
  475. char flags[4];
  476. if(EA::StdC::Sscanf(lineBuffer, "%I64x-%I64x %c%c%c%c %I64x", &start, &end,
  477. &flags[0], &flags[1], &flags[2], &flags[3], &offset) == 7)
  478. {
  479. if((flags[0] == 'r') && (flags[1] == '-') && (flags[2] == 'x')) // If this looks like an executable module...
  480. {
  481. // Note: I don't understand from the Linux documentation what the 'offset' value really means
  482. // and how we are supposed to use it. Example code shows it being subtracted from offset, though
  483. // offset is usually 0.
  484. baseAddress = (start - offset);
  485. break;
  486. }
  487. }
  488. }
  489. }
  490. fclose(file);
  491. }
  492. return baseAddress;
  493. }
  494. */
  495. ///////////////////////////////////////////////////////////////////////////////
  496. // GetModuleHandleFromAddress
  497. //
  498. EATHREADLIB_API ModuleHandle GetModuleHandleFromAddress(const void* /*pAddress*/)
  499. {
  500. // This is doable for Linux-based platforms via fopen("/proc/self/maps")
  501. // Probably also doable for BSD.
  502. // http://freebsd.1045724.n5.nabble.com/How-to-get-stack-bounds-of-current-process-td4053477.html
  503. // Not currently implemented for the given platform.
  504. return 0;
  505. }
  506. EA::Thread::ThreadLocalStorage sStackBase;
  507. ///////////////////////////////////////////////////////////////////////////////
  508. // SetStackBase
  509. //
  510. EATHREADLIB_API void SetStackBase(void* pStackBase)
  511. {
  512. if(pStackBase)
  513. sStackBase.SetValue(pStackBase);
  514. else
  515. {
  516. pStackBase = __builtin_frame_address(0);
  517. if(pStackBase)
  518. SetStackBase(pStackBase);
  519. // Else failure; do nothing.
  520. }
  521. }
  522. ///////////////////////////////////////////////////////////////////////////////
  523. // GetStackBase
  524. //
  525. EATHREADLIB_API void* GetStackBase()
  526. {
  527. void* pBase;
  528. if(GetPthreadStackInfo(&pBase, NULL))
  529. return pBase;
  530. // Else we require the user to have set this previously, usually via a call
  531. // to SetStackBase() in the start function of this currently executing
  532. // thread (or main for the main thread).
  533. pBase = sStackBase.GetValue();
  534. if(pBase == NULL)
  535. pBase = (void*)(((uintptr_t)&pBase + 4095) & ~4095); // Make a guess, round up to next 4096.
  536. return pBase;
  537. }
  538. ///////////////////////////////////////////////////////////////////////////////
  539. // GetStackLimit
  540. //
  541. EATHREADLIB_API void* GetStackLimit()
  542. {
  543. void* pLimit;
  544. if(GetPthreadStackInfo(NULL, &pLimit))
  545. return pLimit;
  546. pLimit = __builtin_frame_address(0);
  547. return (void*)((uintptr_t)pLimit & ~4095); // Round down to nearest page, as the stack grows downward.
  548. }
  549. } // namespace Thread
  550. } // namespace EA