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


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

#if !PPM_USE_EATHREAD_CALLSTACK

#include <EABase/eabase.h>

#include <stdio.h>        // sprintf

#if !defined(EA_PLATFORM_XENON)
#error This file should only be included on the Xenon platform
#endif

#pragma warning(push, 1)
#pragma warning(disable: 4311 4312)
#include <xtl.h>
#if EA_XBDM_ENABLED
    #include <xbdm.h>
    // #pragma comment(lib, "xbdm.lib") The application is expected to provide this if needed.
#endif
#pragma warning(pop)

namespace EA
{
    namespace Allocator
    {

        PPM_API void InitCallstack()
        {
            // Empty
        }


        PPM_API void ShutdownCallstack()
        {
            // Empty
        }


        /// 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)
        {
            #if EA_XBDM_ENABLED

                if(nReturnAddressArrayLength > DM_MAX_STACK_DEPTH)
                    nReturnAddressArrayLength = DM_MAX_STACK_DEPTH;

                if(FAILED(DmCaptureStackBackTrace((ULONG)nReturnAddressArrayLength, pReturnAddressArray)))
                    return 0;

                size_t resultArrayLength = 0;

                while(pReturnAddressArray[resultArrayLength] && resultArrayLength < nReturnAddressArrayLength)
                    ++resultArrayLength;

                return resultArrayLength;

            #else
                size_t     nEntryIndex(0);
                uintptr_t* pFrame;
                void*      pInstruction = NULL;

                uint64_t gpr1;
                __asm std r1, gpr1;
                pFrame = (uintptr_t*)(uintptr_t)gpr1;

                if(pFrame)
                {
                    //GetInstructionPointer(pInstruction); This isn't necessary, but may be useful.

                    for(int nStackIndex = 0; nEntryIndex < (nReturnAddressArrayLength - 1); ++nStackIndex)
                    {
                        if((pInstruction != NULL) || (nStackIndex > 0)) 
                            pReturnAddressArray[nEntryIndex++] = pInstruction;

                        const void* const pPrevFrame = pFrame;

                        pFrame = (uintptr_t*)*pFrame;

                        // If pFrame is 0 or is not aligned as expected (4 bytes) or is 
                        // a pointer to an address thats before pPrevFrame, we quit.
                        if(!pFrame || ((uintptr_t)pFrame & 15) || (pFrame < pPrevFrame))
                            break;

                        pInstruction = (void*)pFrame[-2];
                    }
                }

                pReturnAddressArray[nEntryIndex] = 0;
                return nEntryIndex;
            #endif
        }

    } // namespace Allocator

} // namespace EA

#endif

