/////////////////////////////////////////////////////////////////////////////// // Copyright (c) Electronic Arts Inc. All rights reserved. /////////////////////////////////////////////////////////////////////////////// #include #include #include #include #include #if EATHREAD_GLIBC_BACKTRACE_AVAILABLE #include #include #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