/*************************************************************************** * Copyright (c) 2024 Microsoft Corporation * * 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 */ /* */ /* RELEASE HISTORY */ /* */ /* DATE NAME DESCRIPTION */ /* */ /* 05-19-2020 William E. Lamie Initial Version 6.0 */ /* 09-30-2020 Yuxin Zhou Modified comment(s), */ /* resulting in version 6.1 */ /* */ /**************************************************************************/ 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 }