| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269 |
- ///////////////////////////////////////////////////////////////////////////////
- // Copyright (c) Electronic Arts Inc. All rights reserved.
- ///////////////////////////////////////////////////////////////////////////////
- #include <eathread/eathread_callstack.h>
- #include <eathread/eathread_callstack_context.h>
- #include <string.h>
- #include <pthread.h>
- #include <eathread/eathread_storage.h>
- #if EATHREAD_GLIBC_BACKTRACE_AVAILABLE
- #include <signal.h>
- #include <execinfo.h>
- #endif
- namespace EA
- {
- namespace Thread
- {
- ///////////////////////////////////////////////////////////////////////////////
- // GetInstructionPointer
- //
- EATHREADLIB_API void GetInstructionPointer(void*& pInstruction)
- {
- pInstruction = __builtin_return_address(0);
- }
- ///////////////////////////////////////////////////////////////////////////////
- // InitCallstack
- //
- EATHREADLIB_API void InitCallstack()
- {
- }
- ///////////////////////////////////////////////////////////////////////////////
- // ShutdownCallstack
- //
- EATHREADLIB_API void ShutdownCallstack()
- {
- }
- ///////////////////////////////////////////////////////////////////////////////
- // GetCallstack
- //
- // Capture up to nReturnAddressArrayCapacity elements of the call stack,
- // or the whole callstack, whichever is smaller.
- //
- EATHREADLIB_API size_t GetCallstack(void* pReturnAddressArray[], size_t nReturnAddressArrayCapacity, const CallstackContext* pContext)
- {
- EA_UNUSED(pContext);
- #if EATHREAD_GLIBC_BACKTRACE_AVAILABLE
- size_t count = 0;
- // The pContext option is not currently supported.
- if(pContext == NULL)
- {
- count = (size_t)backtrace(pReturnAddressArray, (int)nReturnAddressArrayCapacity);
- if(count > 0)
- {
- --count; // Remove the first entry, because it refers to this function and by design we don't include this function.
- memmove(pReturnAddressArray, pReturnAddressArray + 1, count * sizeof(void*));
- }
- }
-
- return count;
- #elif defined(EA_PLATFORM_APPLE) && defined(EA_PROCESSOR_X86)
- // Apple's ABI defines a callstack frame system.
- // http://developer.apple.com/library/mac/#documentation/DeveloperTools/Conceptual/LowLevelABI/100-32-bit_PowerPC_Function_Calling_Conventions/32bitPowerPC.html#//apple_ref/doc/uid/TP40002438-SW20
- // Apple x86 stack frame:
- // struct StackFrame {
- // StackFrame* mpParentStackFrame;
- // void* mpParentCR;
- // void* mpReturnPC;
- // }
- //
- //struct StackFrame { To do: Re-write this in terms of StackFrame and pContext.
- // StackFrame* mpParentStackFrame;
- // void* mpParentCR;
- // void* mpReturnPC;
- //};
- size_t index = 0;
- if(nReturnAddressArrayCapacity)
- {
- void* fpParent = __builtin_frame_address(1); // frame pointer of caller.
- void** fp;
- pReturnAddressArray[index++] = __builtin_return_address(0);
- fp = (void**)fpParent;
- while(fp && *fp && (index < nReturnAddressArrayCapacity))
- {
- fpParent = *fp;
- fp = fpParent;
- if(*fp)
- pReturnAddressArray[index++] = *(fp + 2);
- }
- }
- return index;
- #else
- EA_UNUSED(pReturnAddressArray);
- EA_UNUSED(nReturnAddressArrayCapacity);
- return 0;
- #endif
- }
- ///////////////////////////////////////////////////////////////////////////////
- // GetCallstackContext
- //
- EATHREADLIB_API void GetCallstackContext(CallstackContext& context, const Context* pContext)
- {
- #if defined(EA_PROCESSOR_X86_64)
- context.mRIP = pContext->Rip;
- context.mRSP = pContext->Rsp;
- context.mRBP = pContext->Rbp;
- #elif defined(EA_PROCESSOR_X86)
- context.mEIP = pContext->Eip;
- context.mESP = pContext->Esp;
- context.mEBP = pContext->Ebp;
- #elif defined(EA_PROCESSOR_ARM)
- context.mSP = pContext->mGpr[13];
- context.mLR = pContext->mGpr[14];
- context.mPC = pContext->mGpr[15];
- #else
- // To do.
- #endif
- }
- ///////////////////////////////////////////////////////////////////////////////
- // GetModuleFromAddress
- //
- EATHREADLIB_API size_t GetModuleFromAddress(const void* /*address*/, char* pModuleName, size_t /*moduleNameCapacity*/)
- {
- // Not currently implemented for the given platform.
- pModuleName[0] = 0;
- return 0;
- }
- ///////////////////////////////////////////////////////////////////////////////
- // GetModuleHandleFromAddress
- //
- EATHREADLIB_API ModuleHandle GetModuleHandleFromAddress(const void* /*pAddress*/)
- {
- // Not currently implemented for the given platform.
- return 0;
- }
- ///////////////////////////////////////////////////////////////////////////////
- // GetCallstackContext
- //
- // Under Windows, the threadId parameter is expected to be a thread HANDLE,
- // which is different from a windows integer thread id.
- // On Unix the threadId parameter is expected to be a pthread id.
- //
- EATHREADLIB_API bool GetCallstackContext(CallstackContext& context, intptr_t /*threadId*/)
- {
- //if(threadId == pthread_self()) // Note that at least for MacOS, it's possible to get other threads' info.
- {
- #if defined(EA_PROCESSOR_X86)
- context.mEIP = (uint32_t)__builtin_return_address(0);
- context.mESP = (uint32_t)__builtin_frame_address(1);
- context.mEBP = 0;
- #else
- // To do.
- #endif
-
- return true;
- }
-
- // Not currently implemented for the given platform.
- memset(&context, 0, sizeof(context));
- return false;
- }
- ///////////////////////////////////////////////////////////////////////////////
- // GetCallstackContextSysThreadId
- //
- EATHREADLIB_API bool GetCallstackContextSysThreadId(CallstackContext& context, intptr_t sysThreadId)
- {
- return GetCallstackContext(context, sysThreadId);
- }
- // To do: Remove the usage of sStackBase for the platforms that it's not needed,
- // as can be seen from the logic below. For example Mac OSX probably doesn't need it.
- static EA::Thread::ThreadLocalStorage sStackBase;
- ///////////////////////////////////////////////////////////////////////////////
- // SetStackBase
- //
- EATHREADLIB_API void SetStackBase(void* pStackBase)
- {
- if(pStackBase)
- sStackBase.SetValue(pStackBase);
- else
- {
- pStackBase = __builtin_frame_address(0);
- if(pStackBase)
- SetStackBase(pStackBase);
- // Else failure; do nothing.
- }
- }
- ///////////////////////////////////////////////////////////////////////////////
- // GetStackBase
- //
- EATHREADLIB_API void* GetStackBase()
- {
- #if defined(EA_PLATFORM_UNIX)
- void* pBase;
- if(GetPthreadStackInfo(&pBase, NULL))
- return pBase;
- #endif
- // Else we require the user to have set this previously, usually via a call
- // to SetStackBase() in the start function of this currently executing
- // thread (or main for the main thread).
- return sStackBase.GetValue();
- }
- ///////////////////////////////////////////////////////////////////////////////
- // GetStackLimit
- //
- EATHREADLIB_API void* GetStackLimit()
- {
- #if defined(EA_PLATFORM_UNIX)
- void* pLimit;
- if(GetPthreadStackInfo(NULL, &pLimit))
- return pLimit;
- #endif
- // If this fails then we might have an issue where you are using GCC but not
- // using the GCC standard library glibc. Or maybe glibc doesn't support
- // __builtin_frame_address on this platform. Or maybe you aren't using GCC but
- // rather a compiler that masquerades as GCC (common situation).
- void* pStack = __builtin_frame_address(0);
- return (void*)((uintptr_t)pStack & ~4095); // Round down to nearest page, as the stack grows downward.
- }
- } // namespace Thread
- } // namespace EA
|