/***************************************************************************
 * Copyright (c) 2024 Microsoft Corporation
 * Copyright (c) 2026-present Eclipse ThreadX contributors
 *
 * This program and the accompanying materials are made available under the
 * terms of the MIT License which is available at
 * https://opensource.org/licenses/MIT.
 *
 * SPDX-License-Identifier: MIT
 **************************************************************************/


/**************************************************************************/
/**************************************************************************/
/**                                                                       */
/** ThreadX Component                                                     */
/**                                                                       */
/**   Trace                                                               */
/**                                                                       */
/**************************************************************************/
/**************************************************************************/

#define TX_SOURCE_CODE


/* Include necessary system files.  */

#include "tx_api.h"
#ifdef TX_ENABLE_EVENT_TRACE
#include "tx_thread.h"
#include "tx_timer.h"
#include "tx_event_flags.h"
#include "tx_queue.h"
#include "tx_semaphore.h"
#include "tx_mutex.h"
#include "tx_block_pool.h"
#include "tx_byte_pool.h"
#endif
#include "tx_trace.h"


/**************************************************************************/
/*                                                                        */
/*  FUNCTION                                               RELEASE        */
/*                                                                        */
/*    _tx_trace_enable                                    PORTABLE C      */
/*                                                           6.1          */
/*  AUTHOR                                                                */
/*                                                                        */
/*    William E. Lamie, Microsoft Corporation                             */
/*                                                                        */
/*  DESCRIPTION                                                           */
/*                                                                        */
/*    This function initializes the ThreadX trace buffer and the          */
/*    associated control variables, enabling it for operation.            */
/*                                                                        */
/*  INPUT                                                                 */
/*                                                                        */
/*    trace_buffer_start                    Start of trace buffer         */
/*    trace_buffer_size                     Size (bytes) of trace buffer  */
/*    registry_entries                      Number of object registry     */
/*                                            entries.                    */
/*                                                                        */
/*  OUTPUT                                                                */
/*                                                                        */
/*    Completion Status                                                   */
/*                                                                        */
/*  CALLS                                                                 */
/*                                                                        */
/*    _tx_thread_system_preempt_check       Check for preemption          */
/*    _tx_trace_object_register             Register existing objects     */
/*                                                                        */
/*  CALLED BY                                                             */
/*                                                                        */
/*    Application Code                                                    */
/*                                                                        */
/**************************************************************************/
UINT  _tx_trace_enable(VOID *trace_buffer_start, ULONG trace_buffer_size, ULONG registry_entries)
{

#ifdef TX_ENABLE_EVENT_TRACE

TX_INTERRUPT_SAVE_AREA

TX_THREAD                       *thread_ptr;
TX_TIMER                        *timer_ptr;
TX_EVENT_FLAGS_GROUP            *event_flags_ptr;
TX_QUEUE                        *queue_ptr;
TX_SEMAPHORE                    *semaphore_ptr;
TX_MUTEX                        *mutex_ptr;
TX_BLOCK_POOL                   *block_pool_ptr;
TX_BYTE_POOL                    *byte_pool_ptr;
UCHAR                           *work_ptr;
UCHAR                           *event_start_ptr;
TX_TRACE_OBJECT_ENTRY           *entry_ptr;
TX_TRACE_BUFFER_ENTRY           *event_ptr;
ULONG                           i;
UINT                            status;


    /* First, see if there is enough room for the control header, the registry entries, and at least one event in
       memory supplied to this call.  */
    if (trace_buffer_size < ((sizeof(TX_TRACE_HEADER)) + ((sizeof(TX_TRACE_OBJECT_ENTRY)) * registry_entries) + (sizeof(TX_TRACE_BUFFER_ENTRY))))
    {

        /* No, the memory isn't big enough to hold one trace buffer entry.  Return an error.  */
        status =  TX_SIZE_ERROR;
    }

    /* Determine if trace is already enabled.  */
    else if (_tx_trace_buffer_current_ptr != TX_NULL)
    {

        /* Yes, trace is already enabled.  */
        status =  TX_NOT_DONE;
    }
    else
    {

        /* Set the enable bits for all events enabled.  */
        _tx_trace_event_enable_bits =  0xFFFFFFFFUL;

        /* Setup working pointer to the supplied memory.  */
        work_ptr =  TX_VOID_TO_UCHAR_POINTER_CONVERT(trace_buffer_start);

        /* Setup pointer to the trace control area.  */
        _tx_trace_header_ptr =  TX_UCHAR_TO_HEADER_POINTER_CONVERT(work_ptr);

        /* Move the working pointer past the control area.  */
        work_ptr =  TX_UCHAR_POINTER_ADD(work_ptr, (sizeof(TX_TRACE_HEADER)));

        /* Save the start of the trace object registry.  */
        _tx_trace_registry_start_ptr =  TX_UCHAR_TO_OBJECT_POINTER_CONVERT(work_ptr);

        /* Setup the end of the trace object registry.  */
        work_ptr =  TX_UCHAR_POINTER_ADD(work_ptr, (sizeof(TX_TRACE_OBJECT_ENTRY))*registry_entries);
        _tx_trace_registry_end_ptr =  TX_UCHAR_TO_OBJECT_POINTER_CONVERT(work_ptr);

        /* Loop to make all trace object registry entries empty and valid.  */
        for (i = ((ULONG) 0); i < registry_entries; i++)
        {

            /* Setup the work pointer.  */
            work_ptr =  TX_OBJECT_TO_UCHAR_POINTER_CONVERT(_tx_trace_registry_start_ptr);
            work_ptr =  TX_UCHAR_POINTER_ADD(work_ptr, (sizeof(TX_TRACE_OBJECT_ENTRY))*i);

            /* Convert to a registry entry pointer.  */
            entry_ptr =  TX_UCHAR_TO_OBJECT_POINTER_CONVERT(work_ptr);

            /* Initialize object registry entry.  */
            entry_ptr -> tx_trace_object_entry_available =         (UCHAR) TX_TRUE;
            entry_ptr -> tx_trace_object_entry_type =              (UCHAR) TX_TRACE_OBJECT_TYPE_NOT_VALID;
            entry_ptr -> tx_trace_object_entry_reserved1 =         (UCHAR) 0;
            entry_ptr -> tx_trace_object_entry_reserved2 =         (UCHAR) 0;
            entry_ptr -> tx_trace_object_entry_thread_pointer =    (ULONG) 0;
        }

        /* Setup the total number of registry entries.  */
        _tx_trace_total_registry_entries =  registry_entries;

        /* Setup the object registry available count to the total number of registry entries.  */
        _tx_trace_available_registry_entries =  registry_entries;

        /* Setup the search starting index to the first entry.  */
        _tx_trace_registry_search_start =  ((ULONG) 0);

        /* Setup the work pointer to after the trace object registry.  */
        work_ptr =  TX_OBJECT_TO_UCHAR_POINTER_CONVERT(_tx_trace_registry_end_ptr);

        /* Adjust the remaining trace buffer size.  */
        trace_buffer_size =  trace_buffer_size - ((sizeof(TX_TRACE_OBJECT_ENTRY)) * registry_entries) - (sizeof(TX_TRACE_HEADER));

        /* Setup pointer to the start of the actual event trace log.  */
        _tx_trace_buffer_start_ptr =      TX_UCHAR_TO_ENTRY_POINTER_CONVERT(work_ptr);

        /* Save the event trace log start address.  */
        event_start_ptr =  work_ptr;

        /* Calculate the end of the trace buffer.  */
        work_ptr =  TX_UCHAR_POINTER_ADD(work_ptr, ((trace_buffer_size/(sizeof(TX_TRACE_BUFFER_ENTRY)))*(sizeof(TX_TRACE_BUFFER_ENTRY))));
        _tx_trace_buffer_end_ptr =        TX_UCHAR_TO_ENTRY_POINTER_CONVERT(work_ptr);

        /* Loop to mark all entries in the trace buffer as invalid.  */
        for (i = ((ULONG) 0); i < (trace_buffer_size/(sizeof(TX_TRACE_BUFFER_ENTRY))); i++)
        {

            /* Setup the work pointer.  */
            work_ptr =  TX_UCHAR_POINTER_ADD(event_start_ptr, (sizeof(TX_TRACE_BUFFER_ENTRY))*i);

            /* Convert to a trace event pointer.  */
            event_ptr =  TX_UCHAR_TO_ENTRY_POINTER_CONVERT(work_ptr);

            /* Mark this trace event as invalid.  */
            event_ptr -> tx_trace_buffer_entry_thread_pointer =  ((ULONG) 0);
        }

        /* Now, fill in the event trace control header.  */
        _tx_trace_header_ptr -> tx_trace_header_id =                             TX_TRACE_VALID;
        _tx_trace_header_ptr -> tx_trace_header_timer_valid_mask =               TX_TRACE_TIME_MASK;
        _tx_trace_header_ptr -> tx_trace_header_trace_base_address =             TX_POINTER_TO_ULONG_CONVERT(trace_buffer_start);
        _tx_trace_header_ptr -> tx_trace_header_registry_start_pointer =         TX_POINTER_TO_ULONG_CONVERT(_tx_trace_registry_start_ptr);
        _tx_trace_header_ptr -> tx_trace_header_reserved1 =                      ((USHORT) 0);
        _tx_trace_header_ptr -> tx_trace_header_object_name_size =               ((USHORT) TX_TRACE_OBJECT_REGISTRY_NAME);
        _tx_trace_header_ptr -> tx_trace_header_registry_end_pointer =           TX_POINTER_TO_ULONG_CONVERT(_tx_trace_registry_end_ptr);
        _tx_trace_header_ptr -> tx_trace_header_buffer_start_pointer =           TX_POINTER_TO_ULONG_CONVERT(_tx_trace_buffer_start_ptr);
        _tx_trace_header_ptr -> tx_trace_header_buffer_end_pointer =             TX_POINTER_TO_ULONG_CONVERT(_tx_trace_buffer_end_ptr);
        _tx_trace_header_ptr -> tx_trace_header_buffer_current_pointer =         TX_POINTER_TO_ULONG_CONVERT(_tx_trace_buffer_start_ptr);
        _tx_trace_header_ptr -> tx_trace_header_reserved2 =                      0xAAAAAAAAUL;
        _tx_trace_header_ptr -> tx_trace_header_reserved3 =                      0xBBBBBBBBUL;
        _tx_trace_header_ptr -> tx_trace_header_reserved4 =                      0xCCCCCCCCUL;

        /* Now, loop through all existing ThreadX objects and register them in the newly setup trace buffer.  */

        /* Disable interrupts.  */
        TX_DISABLE

        /* First, disable preemption.  */
        _tx_thread_preempt_disable++;

        /* Restore interrupts.  */
        TX_RESTORE

        /* Pickup the first thread and the number of created threads.  */
        thread_ptr =  _tx_thread_created_ptr;
        i =           _tx_thread_created_count;

        /* Loop to register all threads.  */
        while (i != ((ULONG) 0))
        {

            /* Decrement the counter.  */
            i--;

            /* Register this thread.  */
            _tx_trace_object_register(TX_TRACE_OBJECT_TYPE_THREAD, thread_ptr, thread_ptr -> tx_thread_name,
                                        TX_POINTER_TO_ULONG_CONVERT(thread_ptr -> tx_thread_stack_start), (ULONG) thread_ptr -> tx_thread_stack_size);

            /* Move to the next thread.  */
            thread_ptr =  thread_ptr -> tx_thread_created_next;
        }

        /* Pickup the first timer and the number of created timers.  */
        timer_ptr =  _tx_timer_created_ptr;
        i =          _tx_timer_created_count;

        /* Loop to register all timers.  */
        while (i != ((ULONG) 0))
        {

            /* Decrement the counter.  */
            i--;

            /* Register this timer.  */
            _tx_trace_object_register(TX_TRACE_OBJECT_TYPE_TIMER, timer_ptr, timer_ptr -> tx_timer_name,
                                                       ((ULONG) 0), timer_ptr -> tx_timer_internal.tx_timer_internal_re_initialize_ticks);

            /* Move to the next timer.  */
            timer_ptr =  timer_ptr -> tx_timer_created_next;
        }


        /* Pickup the first event flag group and the number of created groups.  */
        event_flags_ptr =  _tx_event_flags_created_ptr;
        i =                _tx_event_flags_created_count;

        /* Loop to register all event flags groups.  */
        while (i != ((ULONG) 0))
        {

            /* Decrement the counter.  */
            i--;

            /* Register this event flags group.  */
            _tx_trace_object_register(TX_TRACE_OBJECT_TYPE_EVENT_FLAGS, event_flags_ptr, event_flags_ptr -> tx_event_flags_group_name, ((ULONG) 0), ((ULONG) 0));

            /* Move to the next event flags group.  */
            event_flags_ptr =  event_flags_ptr -> tx_event_flags_group_created_next;
        }

        /* Pickup the first queue and the number of created queues.  */
        queue_ptr =  _tx_queue_created_ptr;
        i =          _tx_queue_created_count;

        /* Loop to register all queues.  */
        while (i != ((ULONG) 0))
        {

            /* Decrement the counter.  */
            i--;

            /* Register this queue.  */
            _tx_trace_object_register(TX_TRACE_OBJECT_TYPE_QUEUE, queue_ptr, queue_ptr -> tx_queue_name,
                                                                    (queue_ptr -> tx_queue_capacity * (sizeof(ULONG))), ((ULONG) 0));

            /* Move to the next queue.  */
            queue_ptr =  queue_ptr -> tx_queue_created_next;
        }

        /* Pickup the first semaphore and the number of created semaphores.  */
        semaphore_ptr =  _tx_semaphore_created_ptr;
        i =              _tx_semaphore_created_count;

        /* Loop to register all semaphores.  */
        while (i != ((ULONG) 0))
        {

            /* Decrement the counter.  */
            i--;

            /* Register this semaphore.  */
            _tx_trace_object_register(TX_TRACE_OBJECT_TYPE_SEMAPHORE, semaphore_ptr, semaphore_ptr -> tx_semaphore_name, ((ULONG) 0), ((ULONG) 0));

            /* Move to the next semaphore.  */
            semaphore_ptr =  semaphore_ptr -> tx_semaphore_created_next;
        }

        /* Pickup the first mutex and the number of created mutexes.  */
        mutex_ptr =  _tx_mutex_created_ptr;
        i =          _tx_mutex_created_count;

        /* Loop to register all mutexes.  */
        while (i != ((ULONG) 0))
        {

            /* Decrement the counter.  */
            i--;

            /* Register this mutex.  */
            _tx_trace_object_register(TX_TRACE_OBJECT_TYPE_MUTEX, mutex_ptr, mutex_ptr -> tx_mutex_name,
                                                                        (ULONG) mutex_ptr -> tx_mutex_inherit, ((ULONG) 0));

            /* Move to the next mutex.  */
            mutex_ptr =  mutex_ptr -> tx_mutex_created_next;
        }

        /* Pickup the first block pool and the number of created block pools.  */
        block_pool_ptr =  _tx_block_pool_created_ptr;
        i =               _tx_block_pool_created_count;

        /* Loop to register all block pools.  */
        while (i != ((ULONG) 0))
        {

             /* Decrement the counter.  */
            i--;

            /* Register this block pool.  */
            _tx_trace_object_register(TX_TRACE_OBJECT_TYPE_BLOCK_POOL, block_pool_ptr, block_pool_ptr -> tx_block_pool_name,
                                                                            block_pool_ptr -> tx_block_pool_size, ((ULONG) 0));

            /* Move to the next block pool.  */
            block_pool_ptr =  block_pool_ptr -> tx_block_pool_created_next;
        }

        /* Pickup the first byte pool and the number of created byte pools.  */
        byte_pool_ptr =  _tx_byte_pool_created_ptr;
        i =              _tx_byte_pool_created_count;

        /* Loop to register all byte pools.  */
        while (i != ((ULONG) 0))
        {

            /* Decrement the counter.  */
            i--;

            /* Register this byte pool.  */
            _tx_trace_object_register(TX_TRACE_OBJECT_TYPE_BYTE_POOL, byte_pool_ptr, byte_pool_ptr -> tx_byte_pool_name,
                                                                            byte_pool_ptr -> tx_byte_pool_size, ((ULONG) 0));

            /* Move to the next byte pool.  */
            byte_pool_ptr =  byte_pool_ptr -> tx_byte_pool_created_next;
        }

        /* Disable interrupts.  */
        TX_DISABLE

        /* Release the preeemption.  */
        _tx_thread_preempt_disable--;

        /* Finally, setup the current buffer pointer, which effectively enables the trace!  */
        _tx_trace_buffer_current_ptr =    (TX_TRACE_BUFFER_ENTRY *) _tx_trace_buffer_start_ptr;

        /* Insert two RUNNING events so the buffer is not empty.  */
        TX_TRACE_IN_LINE_INSERT(TX_TRACE_RUNNING, 0, 0, 0, 0, TX_TRACE_INTERNAL_EVENTS)
        TX_TRACE_IN_LINE_INSERT(TX_TRACE_RUNNING, 0, 0, 0, 0, TX_TRACE_INTERNAL_EVENTS)

        /* Restore interrupts.  */
        TX_RESTORE

        /* Check for preemption.  */
        _tx_thread_system_preempt_check();

        /* Return successful completion.  */
        status =  TX_SUCCESS;
    }

    /* Return completion status.  */
    return(status);
#else

UINT        status;


    /* Access input arguments just for the sake of lint, MISRA, etc.  */
    if (trace_buffer_start != TX_NULL)
    {

        /* Trace not enabled, return an error.  */
        status =  TX_FEATURE_NOT_ENABLED;
    }
    else if (trace_buffer_size == ((ULONG) 0))
    {

        /* Trace not enabled, return an error.  */
        status =  TX_FEATURE_NOT_ENABLED;
    }
    else if (registry_entries == ((ULONG) 0))
    {

        /* Trace not enabled, return an error.  */
        status =  TX_FEATURE_NOT_ENABLED;
    }
    else
    {

        /* Trace not enabled, return an error.  */
        status =  TX_FEATURE_NOT_ENABLED;
    }

    /* Return completion status.  */
    return(status);
#endif
}



