///////////////////////////////////////////////////////////////////////////////
// EAGeneralAllocatorDebugNGC 
//
// Copyright (c) 2004, Electronic Arts. All Rights Reserved.
// By Paul Pedriana, Maxis
//
// This module implements a platform-specific call stack functionality. 
///////////////////////////////////////////////////////////////////////////////


#include <EABase/eabase.h>
#include <PPMalloc/internal/config.h>
#include <PPMalloc/internal/callstack.h>

#if (defined(EA_PROCESSOR_POWERPC) || defined(EA_PLATFORM_CAFE) || defined(EA_PLATFORM_REVOLUTION)) && !PPM_USE_EATHREAD_CALLSTACK

#include <stddef.h>
#include <stdio.h>



namespace EA
{
    namespace Allocator
    {
        struct StackFrame
        {
            StackFrame* mSavedSP; // Stack pointer.
            uint32_t    mSavedCR; // Condition register.
            uintptr_t*  mSavedLR; // Link register.
        };


        // Function declarations
        PPM_API void   InitCallstack();
        PPM_API void   ShutdownCallstack();
        PPM_API size_t GetCallStack(void* pReturnAddressArray[], size_t nReturnAddressArrayLength);
        PPM_API size_t DescribeCallStack(const void* pReturnAddressArray[], size_t nReturnAddressArrayLength, char* pBuffer, size_t nBufferLength);



        /// InitCallstack
        /// Initializes the callstack reading system for use.
        PPM_API void InitCallstack()
        {
            // We need do nothing for this implementation.
        }


        /// ShutdownCallstack
        /// Shuts down the callstack reading system.
        PPM_API void ShutdownCallstack()
        {
            // We need do nothing for this implementation.
        }


        /// GetCallStack
        /// Returns number of non-zero entries written. The return value may be zero.
        /// Note that the memory size of the entries is thus the return value * sizeof(void).
        /// The first entry in the returned array is the return address of the calling function;
        /// the second entry is the returned addess of the first entry's calling function, etc.
        /// Due to the complications that are often involved in doing stack traces, this function
        /// may return addresses that are invalid (NULL pointers or otherwise). The user should 
        /// be prepared for this and gracefully deal with such addresses.
        ///
        PPM_API size_t GetCallStack(void* pReturnAddressArray[], size_t nReturnAddressArrayLength)
        {
            StackFrame* pStackFrame;
            size_t      i;

            #if defined(EA_ASM_STYLE_ATT) || defined(EA_COMPILER_GNUC)
                __asm__ __volatile__("lwz %0, 0(1)" : "=r" (pStackFrame) : ); // Load into pStack (%0) the contents of what is pointed to by register 1 with 0 offset.
            #elif defined(__MWERKS__)
                uint32_t pStackFrameN; // Question: How do we get Metrowerks to write directly to pStackFrame. It won't.
                asm { 
                    stw r1, pStackFrameN
                }
                pStackFrame = (StackFrame*)pStackFrameN;
            #elif defined(EA_COMPILER_GREEN_HILLS)
                pStackFrame = (StackFrame*)__GETREG(1);
            #else
                pStackFrame = NULL; // We don't have a solution here.
            #endif

            for(i = 0; 
                (i < nReturnAddressArrayLength) && pStackFrame && ((uintptr_t)pStackFrame != 0xffffffff); 
                i++, pStackFrame = pStackFrame->mSavedSP)
            {
                if(pStackFrame->mSavedCR)
                    pReturnAddressArray[i] = (void*)(pStackFrame->mSavedCR - sizeof(uintptr_t));
                else
                    pReturnAddressArray[i] = (void*)pStackFrame->mSavedCR;
            }

            return i;
        }

    } // namespace Allocator

} // namespace EA


#endif // (platform checks)












