///////////////////////////////////////////////////////////////////////////////
// EAGeneralAllocatorDebugGlibc
//
// Copyright (c) 2008, Electronic Arts. All Rights Reserved.
// By Paul Pedriana
//
// 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 <stddef.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>


#ifndef PPM_GLIBC_BACKTRACE_AVAILABLE
    #if defined(__GNUC__) && !defined(__CYGWIN__) && !defined(__MINGW32__) && !defined(EA_PLATFORM_BADA) && !defined(EA_PLATFORM_PLAYBOOK) && !defined(EA_PLATFORM_BSD) && !defined(EA_PLATFORM_PS4)
        #define PPM_GLIBC_BACKTRACE_AVAILABLE 1
    #else
        #define PPM_GLIBC_BACKTRACE_AVAILABLE 0
    #endif
#endif


#if PPM_GLIBC_BACKTRACE_AVAILABLE
    #include <signal.h>
    #include <execinfo.h>
#endif


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 nReturnAddressArrayCapacity)
        {
            #if defined(__GNUC__)
                #if PPM_GLIBC_BACKTRACE_AVAILABLE
                    size_t n = (size_t)backtrace(pReturnAddressArray, (int)nReturnAddressArrayCapacity);
                    return n;
                #else
                    // Until we can get the asm working for GCC, we use __builtin_return_address.
                    // This technique works only if stack frames are enabled, which will be so
                    // if you use -O0 or use -fno-omit-frame-pointer.
                    // Note that the EACallstack package has some code that can read a callstack
                    // via code disassembly. We should use that here instead of the code below.
                    #if defined(EA_PLATFORM_PLAYBOOK)
                        if(nReturnAddressArrayCapacity > 8)
                            nReturnAddressArrayCapacity = 8;

                        switch(nReturnAddressArrayCapacity)
                        {
                            // We have a problem: This will crash on some platforms if the stack isn't that big.
                            case  8: 
                                if (__builtin_frame_address(7) != 0)
                                    pReturnAddressArray[ 7] = __builtin_return_address( 7); // Fall through.
                            case  7: 
                                if (__builtin_frame_address(6) != 0)
                                    pReturnAddressArray[ 6] = __builtin_return_address( 6);
                            case  6:
                                if (__builtin_frame_address(5) != 0)
                                    pReturnAddressArray[ 5] = __builtin_return_address( 5);
                            case  5:
                                if (__builtin_frame_address(4) != 0)
                                    pReturnAddressArray[ 4] = __builtin_return_address( 4);
                            case  4:
                                if (__builtin_frame_address(3) != 0)
                                    pReturnAddressArray[ 3] = __builtin_return_address( 3);
                            case  3:
                                if (__builtin_frame_address(2) != 0)
                                    pReturnAddressArray[ 2] = __builtin_return_address( 2);
                            case  2:
                                if (__builtin_frame_address(1) != 0)
                                    pReturnAddressArray[ 1] = __builtin_return_address( 1);
                            case  1:
                                if (__builtin_frame_address(0) != 0)
                                    pReturnAddressArray[ 0] = __builtin_return_address( 0);
                        }
                    #else
                        if(nReturnAddressArrayCapacity > 8)
                            nReturnAddressArrayCapacity = 8;

                        switch(nReturnAddressArrayCapacity)
                        {
                            // We have a problem: This will crash on some platforms if the stack isn't that big.
                            case  8: pReturnAddressArray[ 7] = __builtin_return_address( 7); // Fall through.
                            case  7: pReturnAddressArray[ 6] = __builtin_return_address( 6);
                            case  6: pReturnAddressArray[ 5] = __builtin_return_address( 5);
                            case  5: pReturnAddressArray[ 4] = __builtin_return_address( 4);
                            case  4: pReturnAddressArray[ 3] = __builtin_return_address( 3);
                            case  3: pReturnAddressArray[ 2] = __builtin_return_address( 2);
                            case  2: pReturnAddressArray[ 1] = __builtin_return_address( 1);
                            case  1: pReturnAddressArray[ 0] = __builtin_return_address( 0);
                        }
                    #endif

                    size_t n = 0;
                    while((n < nReturnAddressArrayCapacity) && pReturnAddressArray[n])
                        ++n;
                    return n;
                #endif
            #else
                EA_UNUSED(pReturnAddressArray);
                EA_UNUSED(nReturnAddressArrayCapacity);
                return 0;
            #endif
        }

    } // namespace Allocator

} // namespace EA

#endif

